mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-15 03:36:51 -04:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c10da25ce4 | ||
|
|
8ab574b7de | ||
|
|
e0ba6e77d1 | ||
|
|
9c289db91d | ||
|
|
b1a5b79d67 | ||
|
|
156fbe9912 | ||
|
|
1f4daf519d |
No files matched your search
@@ -214,6 +214,7 @@ pub fn import(bytes: &[u8], o: &ImportOptions) -> Result<Imported, String> {
|
||||
let (mut document, mut warnings) = build::Build::new(&d, o).run(layout, natural, ratio)?;
|
||||
document.units = if o.fit { unit } else { o.unit.unwrap_or(unit) };
|
||||
warnings.extend(damaged);
|
||||
document.limit_depth_noting(&mut warnings);
|
||||
Ok(Imported { document, warnings })
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,10 @@ pub struct Clip {
|
||||
}
|
||||
|
||||
/// Deepest nesting of clips, and of groups, an importer records ([`nest`] goes one level deeper
|
||||
/// for each): deeper ones are left out.
|
||||
pub const MAX_NEST: usize = 128;
|
||||
/// for each): deeper ones are left out. As deep as a document's objects may nest
|
||||
/// ([`crate::MAX_DEPTH`]) under a layer, around the art; clips in groups that deep together are
|
||||
/// merged by [`Document::limit_depth`].
|
||||
pub const MAX_NEST: usize = crate::MAX_DEPTH - 2;
|
||||
|
||||
/// The warning for clips left out for nesting deeper than [`MAX_NEST`].
|
||||
pub fn deep_clips() -> String {
|
||||
|
||||
@@ -0,0 +1,588 @@
|
||||
//! How deep objects may nest.
|
||||
//!
|
||||
//! The tree is walked by recursion everywhere (drawing, bounds, hit tests, saving, exporting), so
|
||||
//! a tree of unbounded depth overflows the stack: an abort no guard catches. No document deeper
|
||||
//! than [`MAX_DEPTH`] is ever kept, nor one whose symbols and patterns draw deeper than
|
||||
//! [`MAX_DRAWN_DEPTH`] (a symbol holding an instance of itself, or a pattern painting its own
|
||||
//! tile, draws without end): importers and the native reader merge deeper objects into the group
|
||||
//! around them and leave such instances and paints out
|
||||
//! ([`Document::limit_depth`], with [`merged_note`] and [`symbols_note`] as their warnings), and
|
||||
//! the engine refuses an edit that would nest deeper ([`Document::too_deep`]).
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use vectorcraft_color::Paint;
|
||||
|
||||
use crate::{Document, Node, NodeKind};
|
||||
|
||||
/// The deepest an object may be: a top-level layer is 1 deep, what it holds 2, and so on (the art
|
||||
/// of an opacity mask is one deeper than the object it masks). A symbol's or pattern's art counts
|
||||
/// from 1 again.
|
||||
///
|
||||
/// A native file writes three levels of JSON for each level of objects, and its reader stops at
|
||||
/// 128 levels: 32 leaves the deepest object 29 of them for its own data, so every document that
|
||||
/// can be made can be saved and opened again.
|
||||
pub const MAX_DEPTH: usize = 32;
|
||||
|
||||
/// The deepest an object may be drawn: as [`MAX_DEPTH`], the art of a symbol counted under its
|
||||
/// instance and a pattern's tile under the object it paints (and what those use, under them).
|
||||
/// Symbols a few objects deep nest in one another dozens deep within it.
|
||||
pub const MAX_DRAWN_DEPTH: usize = 4 * MAX_DEPTH;
|
||||
|
||||
/// The warning of a file whose objects were nested deeper than [`MAX_DEPTH`].
|
||||
pub fn merged_note() -> String {
|
||||
format!("objects nested more than {MAX_DEPTH} deep were merged into the group around them")
|
||||
}
|
||||
|
||||
/// The warning of a file with symbols or patterns that use themselves or are drawn deeper than
|
||||
/// [`MAX_DRAWN_DEPTH`].
|
||||
pub fn symbols_note() -> String {
|
||||
format!(
|
||||
"symbol instances and pattern paints inside their own symbol or pattern, or drawn more than {MAX_DRAWN_DEPTH} objects deep, were left out"
|
||||
)
|
||||
}
|
||||
|
||||
/// Run `f` where recursion has room: on a thread of its own with a 16 MB stack (the main thread
|
||||
/// has 1 MB on Windows), or here where there are no threads (the web) or none can be started. For
|
||||
/// readers whose recursion is capped but heavy; a panic in `f` goes on to the caller.
|
||||
pub fn with_deep_stack<T: Send>(f: impl Fn() -> T + Sync) -> T {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let threaded = std::thread::scope(|s| {
|
||||
let thread = std::thread::Builder::new().stack_size(16 << 20).spawn_scoped(s, &f).ok()?;
|
||||
Some(thread.join().unwrap_or_else(|panic| std::panic::resume_unwind(panic)))
|
||||
});
|
||||
if let Some(done) = threaded {
|
||||
return done;
|
||||
}
|
||||
}
|
||||
f()
|
||||
}
|
||||
|
||||
/// Is anything in `now` (each `depth` deep) deeper than [`MAX_DEPTH`], or `over` (given its
|
||||
/// depth)? Without recursion: the trees may be of any depth. With `before` (the same list before
|
||||
/// an edit, within the caps), only what the edit changed is looked at: a subtree still shared with
|
||||
/// it, in the same place, is as it was.
|
||||
fn deeper(now: &[Arc<Node>], before: Option<&[Arc<Node>]>, depth: usize, over: &dyn Fn(&Node, usize) -> bool) -> bool {
|
||||
type Pending<'a> = Vec<(&'a Node, Option<&'a Node>, usize)>;
|
||||
fn push<'a>(stack: &mut Pending<'a>, now: &'a [Arc<Node>], before: Option<&'a [Arc<Node>]>, depth: usize) {
|
||||
for (i, n) in now.iter().enumerate() {
|
||||
let was = before.and_then(|b| b.get(i));
|
||||
if !was.is_some_and(|w| Arc::ptr_eq(w, n)) {
|
||||
stack.push((n, was.map(|w| &**w), depth));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut stack = vec![];
|
||||
push(&mut stack, now, before, depth);
|
||||
while let Some((n, was, d)) = stack.pop() {
|
||||
if d > MAX_DEPTH || over(n, d) {
|
||||
return true;
|
||||
}
|
||||
if let Some(ch) = n.children() {
|
||||
push(&mut stack, ch, was.and_then(Node::children).map(Vec::as_slice), d + 1);
|
||||
}
|
||||
if let Some(m) = &n.extras().mask {
|
||||
let was = was.and_then(|w| w.extras().mask.as_ref()).map(|m| std::slice::from_ref(&m.art));
|
||||
push(&mut stack, std::slice::from_ref(&m.art), was, d + 1);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// [`deeper`] for the nesting alone.
|
||||
fn nested_deeper(now: &[Arc<Node>], depth: usize) -> bool {
|
||||
deeper(now, None, depth, &|_, _| false)
|
||||
}
|
||||
|
||||
/// Merge what is deeper than [`MAX_DEPTH`] in `nodes` (each `depth` deep) into the group around
|
||||
/// it: a container at the cap is replaced by what it holds (a clipping group's clipping path and
|
||||
/// an opacity mask too deep are left out); what it held stays hidden or locked as it was. Returns
|
||||
/// whether anything changed. Recurses no deeper than the cap.
|
||||
fn limit(nodes: &mut Vec<Arc<Node>>, depth: usize) -> bool {
|
||||
let mut changed = false;
|
||||
let mut i = 0;
|
||||
while let Some(n) = nodes.get_mut(i) {
|
||||
if !nested_deeper(std::slice::from_ref(n), depth) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
changed = true;
|
||||
let n = Arc::make_mut(n);
|
||||
if n.extras().mask.as_ref().is_some_and(|m| nested_deeper(std::slice::from_ref(&m.art), depth + 1)) {
|
||||
n.extras_mut().mask = None;
|
||||
}
|
||||
if depth < MAX_DEPTH {
|
||||
if let Some(ch) = n.children_mut() {
|
||||
limit(ch, depth + 1);
|
||||
}
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
let clip = matches!(n.kind, NodeKind::Group { clip: true, .. } | NodeKind::Layer { clip: true, .. });
|
||||
let (visible, locked) = (n.visible, n.locked);
|
||||
let mut held = n.children_mut().map(std::mem::take).unwrap_or_default();
|
||||
if clip && !held.is_empty() {
|
||||
held.remove(0);
|
||||
}
|
||||
for c in &mut held {
|
||||
if (!visible && c.visible) || (locked && !c.locked) {
|
||||
let c = Arc::make_mut(c);
|
||||
c.visible &= visible;
|
||||
c.locked |= locked;
|
||||
}
|
||||
}
|
||||
// What it held takes its place, and is looked at in its turn.
|
||||
nodes.splice(i..=i, held);
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
/// A symbol (`false`) or a pattern (`true`) of the document, by name: what art draws by name, an
|
||||
/// instance its symbol's art and a pattern paint its tile's.
|
||||
type Def<'a> = (bool, &'a str);
|
||||
|
||||
/// How deep each symbol's and pattern's art is drawn, the art of the symbols and patterns it uses
|
||||
/// included (drawn under the object that uses them): `None` for one that uses itself, in its art
|
||||
/// or through what that uses, or is drawn deeper than [`MAX_DRAWN_DEPTH`].
|
||||
type Heights<'a> = HashMap<Def<'a>, Option<usize>>;
|
||||
|
||||
/// What `n` itself draws by name: the symbol it is an instance of, the patterns that paint it.
|
||||
fn uses(n: &Node) -> impl Iterator<Item = Def<'_>> {
|
||||
let symbol = match &n.kind {
|
||||
NodeKind::SymbolInstance { symbol, .. } => Some((false, symbol.as_str())),
|
||||
_ => None,
|
||||
};
|
||||
let patterns = crate::swatches::node_paints(n).filter_map(|p| match p {
|
||||
Paint::Pattern { pattern, .. } => Some((true, pattern.as_str())),
|
||||
_ => None,
|
||||
});
|
||||
symbol.into_iter().chain(patterns)
|
||||
}
|
||||
|
||||
/// [`Heights`] of the document's symbols and patterns. Without recursion: they may use one
|
||||
/// another in a chain of any length. Their own art is within [`MAX_DEPTH`].
|
||||
fn heights(doc: &Document) -> Heights<'_> {
|
||||
// Each one's own depth, and what its art uses with the deepest use of each.
|
||||
type Own<'a> = (usize, Vec<(Def<'a>, usize)>);
|
||||
let arts = (doc.symbols.iter().map(|s| ((false, s.name.as_str()), std::slice::from_ref(&s.art))))
|
||||
.chain(doc.patterns.iter().map(|p| ((true, p.name.as_str()), &p.art[..])));
|
||||
let mut own: HashMap<Def, Own> = HashMap::new();
|
||||
for (def, art) in arts {
|
||||
let (mut deepest, mut used) = (0, HashMap::<Def, usize>::new());
|
||||
let mut stack: Vec<(&Node, usize)> = art.iter().map(|n| (&**n, 1)).collect();
|
||||
while let Some((n, d)) = stack.pop() {
|
||||
deepest = deepest.max(d);
|
||||
for u in uses(n) {
|
||||
let at = used.entry(u).or_default();
|
||||
*at = (*at).max(d);
|
||||
}
|
||||
stack.extend(n.children().into_iter().flatten().chain(n.extras().mask.as_ref().map(|m| &m.art)).map(|c| (&**c, d + 1)));
|
||||
}
|
||||
// (The first of two of one name is the one drawn.)
|
||||
own.entry(def).or_insert((deepest, used.into_iter().collect()));
|
||||
}
|
||||
// Depth first over what uses what. One met again while it is being worked out (`None` in its
|
||||
// entry) uses itself.
|
||||
let mut found: HashMap<Def, Option<Option<usize>>> = HashMap::new();
|
||||
let starts: Vec<Def> = own.keys().copied().collect();
|
||||
for start in starts {
|
||||
if found.contains_key(&start) {
|
||||
continue;
|
||||
}
|
||||
// (which, the next of its uses to look at, the deepest found so far: `None` once it can't be drawn)
|
||||
let mut path: Vec<(Def, usize, Option<usize>)> = vec![(start, 0, own.get(&start).map(|o| o.0))];
|
||||
found.insert(start, None);
|
||||
while let Some(&(def, next, deepest)) = path.last() {
|
||||
let used = own.get(&def).map_or(&[][..], |o| &o.1);
|
||||
let Some(&(other, at)) = used.get(next) else {
|
||||
// Everything it uses is worked out.
|
||||
let deepest = deepest.filter(|d| *d <= MAX_DRAWN_DEPTH);
|
||||
found.insert(def, Some(deepest));
|
||||
path.pop();
|
||||
if let Some((user, next, held)) = path.last_mut() {
|
||||
let at = own.get(user).and_then(|o| o.1.get(next.wrapping_sub(1))).map_or(1, |u| u.1);
|
||||
*held = held.zip(deepest).map(|(h, d)| h.max(at + d));
|
||||
}
|
||||
continue;
|
||||
};
|
||||
let known = found.get(&other).copied();
|
||||
if known.is_none() && own.contains_key(&other) {
|
||||
found.insert(other, None);
|
||||
}
|
||||
let Some((_, next, deepest)) = path.last_mut() else { break };
|
||||
*next += 1;
|
||||
match known {
|
||||
// Not in the document: it draws nothing.
|
||||
None if !own.contains_key(&other) => {}
|
||||
None => path.push((other, 0, own.get(&other).map(|o| o.0))),
|
||||
Some(Some(Some(d))) => *deepest = deepest.map(|h| h.max(at + d)),
|
||||
// It uses itself (it is on the way down here), or can't be drawn.
|
||||
Some(None | Some(None)) => *deepest = None,
|
||||
}
|
||||
}
|
||||
}
|
||||
found.into_iter().map(|(def, h)| (def, h.flatten())).collect()
|
||||
}
|
||||
|
||||
/// Can't `used`, by an object `depth` deep, be drawn there?
|
||||
fn undrawable(heights: &Heights, used: Def, depth: usize) -> bool {
|
||||
heights.get(&used).is_some_and(|h| h.is_none_or(|h| depth + h > MAX_DRAWN_DEPTH))
|
||||
}
|
||||
|
||||
/// Take out of `nodes` (each `depth` deep) the uses `bad` picks (given the depth of the object):
|
||||
/// an instance goes, a pattern paint becomes no paint. Returns whether anything changed. For trees
|
||||
/// within [`MAX_DEPTH`].
|
||||
fn strip(nodes: &mut Vec<Arc<Node>>, depth: usize, bad: &dyn Fn(Def, usize) -> bool) -> bool {
|
||||
let any = |n: &Node, d: usize| uses(n).any(|u| bad(u, d));
|
||||
let before = nodes.len();
|
||||
nodes.retain(|n| !matches!(&n.kind, NodeKind::SymbolInstance { symbol, .. } if bad((false, symbol.as_str()), depth)));
|
||||
let mut changed = nodes.len() != before;
|
||||
for n in nodes.iter_mut() {
|
||||
if !deeper(std::slice::from_ref(n), None, depth, &any) {
|
||||
continue;
|
||||
}
|
||||
changed = true;
|
||||
let n = Arc::make_mut(n);
|
||||
if uses(n).any(|u| bad(u, depth)) {
|
||||
for p in crate::swatches::node_paints_mut(n) {
|
||||
if matches!(p, Paint::Pattern { pattern, .. } if bad((true, pattern.as_str()), depth)) {
|
||||
*p = Paint::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
// An opacity mask whose art is such an instance goes with it.
|
||||
let mut mask = n.extras().mask.as_ref().map(|m| vec![m.art.clone()]);
|
||||
if let Some(art) = &mut mask
|
||||
&& strip(art, depth + 1, bad)
|
||||
{
|
||||
match art.pop() {
|
||||
Some(art) => {
|
||||
if let Some(m) = n.extras_mut().mask.as_mut() {
|
||||
m.art = art;
|
||||
}
|
||||
}
|
||||
None => n.extras_mut().mask = None,
|
||||
}
|
||||
}
|
||||
if let Some(ch) = n.children_mut() {
|
||||
strip(ch, depth + 1, bad);
|
||||
}
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
impl Document {
|
||||
/// Is anything nested deeper than [`MAX_DEPTH`], or drawn deeper than [`MAX_DRAWN_DEPTH`]
|
||||
/// (a symbol or pattern that uses itself is)? With `before` (this document before an edit,
|
||||
/// itself within the caps), only what the edit changed is looked at.
|
||||
pub fn too_deep(&self, before: Option<&Document>) -> bool {
|
||||
fn symbol(d: &Document, i: usize) -> Option<&[Arc<Node>]> {
|
||||
d.symbols.get(i).map(|s| std::slice::from_ref(&s.art))
|
||||
}
|
||||
fn pattern(d: &Document, i: usize) -> Option<&[Arc<Node>]> {
|
||||
d.patterns.get(i).map(|p| &p.art[..])
|
||||
}
|
||||
let nested = |_: &Node, _: usize| false;
|
||||
if (0..self.symbols.len()).any(|i| symbol(self, i).is_some_and(|art| deeper(art, before.and_then(|b| symbol(b, i)), 1, &nested)))
|
||||
|| (0..self.patterns.len()).any(|i| pattern(self, i).is_some_and(|art| deeper(art, before.and_then(|b| pattern(b, i)), 1, &nested)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if self.symbols.is_empty() && self.patterns.is_empty() {
|
||||
return deeper(&self.layers, before.map(|b| &b.layers[..]), 1, &nested);
|
||||
}
|
||||
let heights = heights(self);
|
||||
if heights.values().any(Option::is_none) {
|
||||
return true;
|
||||
}
|
||||
// Symbols and patterns draw as deep as before when none changed; else every use of them
|
||||
// is looked at again.
|
||||
let same = |x: &[Arc<Node>], y: &[Arc<Node>]| x.len() == y.len() && x.iter().zip(y).all(|(x, y)| Arc::ptr_eq(x, y));
|
||||
let before = before.filter(|b| {
|
||||
b.symbols.len() == self.symbols.len()
|
||||
&& b.patterns.len() == self.patterns.len()
|
||||
&& b.symbols.iter().zip(&self.symbols).all(|(x, y)| x.name == y.name && Arc::ptr_eq(&x.art, &y.art))
|
||||
&& b.patterns.iter().zip(&self.patterns).all(|(x, y)| x.name == y.name && same(&x.art, &y.art))
|
||||
});
|
||||
let over = |n: &Node, d: usize| uses(n).any(|u| undrawable(&heights, u, d));
|
||||
deeper(&self.layers, before.map(|b| &b.layers[..]), 1, &over)
|
||||
}
|
||||
|
||||
/// [`Self::limit_depth`] → whether objects were merged, and whether uses of symbols and
|
||||
/// patterns were left out.
|
||||
fn limit_depths(&mut self) -> (bool, bool) {
|
||||
let mut merged = limit(&mut self.layers, 1);
|
||||
for s in &mut self.symbols {
|
||||
// The art itself is 1 deep, so it stays the one object it is.
|
||||
let mut art = vec![s.art.clone()];
|
||||
if limit(&mut art, 1)
|
||||
&& let Some(one) = art.pop()
|
||||
{
|
||||
s.art = one;
|
||||
merged = true;
|
||||
}
|
||||
}
|
||||
for p in &mut self.patterns {
|
||||
merged |= limit(&mut p.art, 1);
|
||||
}
|
||||
if self.symbols.is_empty() && self.patterns.is_empty() {
|
||||
return (merged, false);
|
||||
}
|
||||
// The symbols and patterns that can't be drawn lose what their art uses: each is then as
|
||||
// deep as its own art, and what uses it can be drawn.
|
||||
let (symbols, patterns): (Vec<usize>, Vec<usize>) = {
|
||||
let heights = heights(self);
|
||||
let bad = |def: Def| heights.get(&def).is_some_and(Option::is_none);
|
||||
(
|
||||
(0..self.symbols.len()).filter(|i| self.symbols.get(*i).is_some_and(|s| bad((false, s.name.as_str())))).collect(),
|
||||
(0..self.patterns.len()).filter(|i| self.patterns.get(*i).is_some_and(|p| bad((true, p.name.as_str())))).collect(),
|
||||
)
|
||||
};
|
||||
let mut left_out = !(symbols.is_empty() && patterns.is_empty());
|
||||
let all = |_: Def, _: usize| true;
|
||||
for i in symbols {
|
||||
let Some(s) = self.symbols.get_mut(i) else { continue };
|
||||
let mut art = vec![s.art.clone()];
|
||||
strip(&mut art, 1, &all);
|
||||
// (An instance alone was the whole art: an empty group stands in.)
|
||||
s.art = art.pop().unwrap_or_else(|| Arc::new(Node::group(s.art.id, vec![])));
|
||||
}
|
||||
for i in patterns {
|
||||
if let Some(p) = self.patterns.get_mut(i) {
|
||||
strip(&mut p.art, 1, &all);
|
||||
}
|
||||
}
|
||||
// Then the uses drawn too deep where they are.
|
||||
let mut layers = self.layers.clone();
|
||||
left_out |= {
|
||||
let heights = heights(self);
|
||||
strip(&mut layers, 1, &|used, depth| undrawable(&heights, used, depth))
|
||||
};
|
||||
self.layers = layers;
|
||||
(merged, left_out)
|
||||
}
|
||||
|
||||
/// Bring the document within the caps: merge the objects nested deeper than [`MAX_DEPTH`]
|
||||
/// into the group around them, and leave out the uses of symbols and patterns that can't be
|
||||
/// drawn (inside their own symbol or pattern, or deeper than [`MAX_DRAWN_DEPTH`]). What an
|
||||
/// importer does to what it read. Returns whether anything changed.
|
||||
pub fn limit_depth(&mut self) -> bool {
|
||||
let (merged, left_out) = self.limit_depths();
|
||||
merged || left_out
|
||||
}
|
||||
|
||||
/// [`Self::limit_depth`], adding [`merged_note`] and [`symbols_note`] to an importer's
|
||||
/// `warnings` for what it did.
|
||||
pub fn limit_depth_noting(&mut self, warnings: &mut Vec<String>) {
|
||||
let (merged, left_out) = self.limit_depths();
|
||||
warnings.extend(merged.then(merged_note));
|
||||
warnings.extend(left_out.then(symbols_note));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{NodeId, OpacityMask};
|
||||
|
||||
/// One path in `levels` groups, each holding a second path beside the next group.
|
||||
fn nested(levels: usize) -> Arc<Node> {
|
||||
let mut id = 0;
|
||||
let mut next = || {
|
||||
id += 1;
|
||||
NodeId(id)
|
||||
};
|
||||
let path = |id| Arc::new(Node::path(id, Default::default(), Default::default()));
|
||||
let mut inner = path(next());
|
||||
for _ in 0..levels {
|
||||
inner = Arc::new(Node::group(next(), vec![inner, path(next())]));
|
||||
}
|
||||
inner
|
||||
}
|
||||
|
||||
fn doc(art: Arc<Node>) -> Document {
|
||||
let mut d = Document::new(100.0, 100.0);
|
||||
if let Some(ch) = d.layers.first_mut().and_then(|l| Arc::make_mut(l).children_mut()) {
|
||||
ch.push(art);
|
||||
}
|
||||
d
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_document_at_the_cap_is_left_alone() {
|
||||
// The layer, the groups and the path in them: MAX_DEPTH deep.
|
||||
let mut d = doc(nested(MAX_DEPTH - 2));
|
||||
let before = d.clone();
|
||||
assert!(!d.too_deep(None));
|
||||
assert!(!d.limit_depth());
|
||||
assert_eq!(d, before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deeper_objects_are_merged_into_the_group_around_them() {
|
||||
for levels in [MAX_DEPTH - 1, MAX_DEPTH + 5, 100_000] {
|
||||
let mut d = doc(nested(levels));
|
||||
assert!(d.too_deep(None), "{levels}");
|
||||
assert!(d.limit_depth(), "{levels}");
|
||||
assert!(!d.too_deep(None), "{levels}");
|
||||
// Every path is kept, under the layer and the groups down to the cap.
|
||||
assert_eq!(d.node_count(), 1 + (MAX_DEPTH - 2) + (levels + 1), "{levels}");
|
||||
assert!(!d.limit_depth(), "nothing more to merge");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_clipping_path_and_a_mask_too_deep_are_left_out_and_hidden_art_stays_hidden() {
|
||||
let mut inner = Node::new(NodeId(9000), NodeKind::Group { children: vec![nested(0), nested(0)], clip: true });
|
||||
inner.visible = false;
|
||||
let mut art = Arc::new(inner);
|
||||
for i in 0..MAX_DEPTH {
|
||||
art = Arc::new(Node::group(NodeId(9001 + i as u64), vec![art]));
|
||||
}
|
||||
let mut masked = Node::group(NodeId(8000), vec![]);
|
||||
masked.extras_mut().mask = Some(Box::new(OpacityMask::new((*nested(MAX_DEPTH)).clone(), true)));
|
||||
let mut d = doc(art);
|
||||
if let Some(ch) = d.layers.first_mut().and_then(|l| Arc::make_mut(l).children_mut()) {
|
||||
ch.push(Arc::new(masked));
|
||||
}
|
||||
assert!(d.limit_depth());
|
||||
assert!(!d.too_deep(None));
|
||||
let mut leaves = vec![];
|
||||
d.walk(|n| {
|
||||
if n.children().is_none() {
|
||||
leaves.push(n.visible);
|
||||
}
|
||||
});
|
||||
assert_eq!(leaves, [false], "the clipping path is gone, what it clipped is still hidden");
|
||||
assert!(d.node(NodeId(8000)).is_some_and(|n| n.extras().mask.is_none()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_edit_is_checked_by_what_it_changed() {
|
||||
let before = doc(nested(MAX_DEPTH - 2));
|
||||
let mut wrapped = before.clone();
|
||||
if let Some(ch) = wrapped.layers.first_mut().and_then(|l| Arc::make_mut(l).children_mut()) {
|
||||
let all = std::mem::take(ch);
|
||||
ch.push(Arc::new(Node::group(NodeId(7000), all)));
|
||||
}
|
||||
assert!(wrapped.too_deep(Some(&before)), "the same art, one deeper");
|
||||
let mut beside = before.clone();
|
||||
if let Some(ch) = beside.layers.first_mut().and_then(|l| Arc::make_mut(l).children_mut()) {
|
||||
ch.push(nested(3));
|
||||
}
|
||||
assert!(!beside.too_deep(Some(&before)));
|
||||
assert!(!before.too_deep(Some(&before)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbols_and_patterns_are_limited_too() {
|
||||
let mut d = Document::new(10.0, 10.0);
|
||||
d.symbols.push(crate::Symbol { name: "s".into(), art: nested(MAX_DEPTH + 3) });
|
||||
assert!(d.too_deep(None));
|
||||
assert!(d.limit_depth());
|
||||
assert!(!d.too_deep(None));
|
||||
assert!(d.symbols.first().is_some_and(|s| s.art.children().is_some()), "still one object");
|
||||
}
|
||||
|
||||
fn instance(id: u64, of: &str) -> Arc<Node> {
|
||||
Arc::new(Node::new(NodeId(id), NodeKind::SymbolInstance { symbol: of.into(), xf: Default::default() }))
|
||||
}
|
||||
|
||||
/// `count` symbols, each `levels` groups around an instance of the next; the last one's of `last`.
|
||||
fn symbols(count: usize, levels: usize, last: &str) -> Document {
|
||||
let mut d = doc(instance(1, "s0"));
|
||||
for i in 0..count {
|
||||
let mut art = instance(2, &if i + 1 < count { format!("s{}", i + 1) } else { last.to_string() });
|
||||
for _ in 0..levels {
|
||||
art = Arc::new(Node::group(NodeId(3), vec![art]));
|
||||
}
|
||||
d.symbols.push(crate::Symbol { name: format!("s{i}"), art });
|
||||
}
|
||||
d
|
||||
}
|
||||
|
||||
fn instances(d: &Document) -> usize {
|
||||
let mut n = 0;
|
||||
let mut count = |x: &Node| n += usize::from(matches!(x.kind, NodeKind::SymbolInstance { .. }));
|
||||
d.walk(&mut count);
|
||||
d.symbols.iter().for_each(|s| s.art.walk(&mut count));
|
||||
n
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbols_in_symbols_are_drawn_only_so_deep() {
|
||||
// Three objects deep each (two groups and the instance), under the instance on the layer.
|
||||
let fits = MAX_DRAWN_DEPTH / 3 - 1;
|
||||
let mut d = symbols(fits, 2, "none");
|
||||
assert!(!d.too_deep(None) && !d.limit_depth());
|
||||
let mut d = symbols(fits + 2, 2, "none");
|
||||
assert!(d.too_deep(None));
|
||||
let before = instances(&d);
|
||||
assert!(d.limit_depth() && !d.too_deep(None));
|
||||
assert!(instances(&d) < before && instances(&d) > 0, "only the instances drawn too deep go");
|
||||
// A chain of any length is measured, not walked by recursion.
|
||||
let mut d = symbols(100_000, 0, "none");
|
||||
assert!(d.too_deep(None) && d.limit_depth() && !d.too_deep(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_symbol_holding_itself_loses_the_instance() {
|
||||
for (count, last) in [(1, "s0"), (3, "s0"), (3, "s1")] {
|
||||
let mut d = symbols(count, 1, last);
|
||||
assert!(d.too_deep(None), "{count} {last}");
|
||||
let mut warnings = vec![];
|
||||
d.limit_depth_noting(&mut warnings);
|
||||
assert_eq!(warnings, [symbols_note()]);
|
||||
assert!(!d.too_deep(None), "{count} {last}");
|
||||
assert!(d.symbols.iter().all(|s| s.art.children().is_some()), "the art is still a group");
|
||||
}
|
||||
// An edit that makes one hold itself is seen, whatever else stayed as it was.
|
||||
let before = symbols(2, 1, "none");
|
||||
let mut after = before.clone();
|
||||
after.symbols[1].art = Arc::new(Node::group(NodeId(9), vec![instance(8, "s0")]));
|
||||
assert!(!before.too_deep(None) && after.too_deep(Some(&before)));
|
||||
}
|
||||
|
||||
/// A path painted with the pattern named `with`.
|
||||
fn painted(id: u64, with: &str) -> Arc<Node> {
|
||||
let fill = Paint::Pattern { pattern: with.into(), xf: Default::default() };
|
||||
Arc::new(Node::path(NodeId(id), Default::default(), crate::Appearance::basic(fill, Paint::None, 0.0)))
|
||||
}
|
||||
|
||||
fn pattern_fills(d: &Document) -> usize {
|
||||
let mut n = 0;
|
||||
d.visit_paints(&mut |p| n += usize::from(matches!(p, Paint::Pattern { .. })));
|
||||
n
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_pattern_painting_its_own_tile_loses_the_paint() {
|
||||
// One that paints its own tile; two that paint each other's; one in a symbol it holds.
|
||||
let itself = |d: &mut Document| d.patterns.push(crate::PatternDef::new("p", vec![painted(2, "p")]));
|
||||
let each_other = |d: &mut Document| {
|
||||
d.patterns.push(crate::PatternDef::new("p", vec![painted(2, "q")]));
|
||||
d.patterns.push(crate::PatternDef::new("q", vec![painted(3, "p")]));
|
||||
};
|
||||
let through_a_symbol = |d: &mut Document| {
|
||||
d.patterns.push(crate::PatternDef::new("p", vec![instance(2, "s")]));
|
||||
d.symbols.push(crate::Symbol { name: "s".into(), art: painted(3, "p") });
|
||||
};
|
||||
for make in [&itself as &dyn Fn(&mut Document), &each_other, &through_a_symbol] {
|
||||
let mut d = doc(painted(1, "p"));
|
||||
make(&mut d);
|
||||
assert!(d.too_deep(None));
|
||||
let mut warnings = vec![];
|
||||
d.limit_depth_noting(&mut warnings);
|
||||
assert_eq!(warnings, [symbols_note()]);
|
||||
assert!(!d.too_deep(None));
|
||||
assert_eq!(pattern_fills(&d), 1, "the object on the layer keeps its pattern");
|
||||
}
|
||||
// A pattern whose tile is painted with another is fine.
|
||||
let mut d = doc(painted(1, "p"));
|
||||
d.patterns.push(crate::PatternDef::new("p", vec![painted(2, "q")]));
|
||||
d.patterns.push(crate::PatternDef::new("q", vec![painted(3, "none")]));
|
||||
assert!(!d.too_deep(None) && !d.limit_depth());
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ pub mod blend;
|
||||
pub mod clipnest;
|
||||
pub mod cmyk;
|
||||
pub mod corners;
|
||||
pub mod depth;
|
||||
pub mod graph;
|
||||
pub mod hit;
|
||||
mod id_index;
|
||||
@@ -79,6 +80,7 @@ pub use appearance::{
|
||||
};
|
||||
pub use assets::ExportAsset;
|
||||
pub use corners::LiveCorners;
|
||||
pub use depth::MAX_DEPTH;
|
||||
pub use graph::{
|
||||
GraphDesign, GraphKind, GraphSpec, LabelStyles, MAX_GRAPH_CATEGORIES, MAX_GRAPH_SERIES, PiePosition, PieSort, SeriesPaint, TickLength,
|
||||
ValueAxisSide,
|
||||
|
||||
@@ -224,7 +224,7 @@ fn item_paint_mut(it: &mut AppearanceItem) -> &mut Paint {
|
||||
}
|
||||
|
||||
/// The paints a node holds itself (not its children): appearance items, then text runs.
|
||||
fn node_paints(n: &Node) -> impl Iterator<Item = &Paint> {
|
||||
pub(crate) fn node_paints(n: &Node) -> impl Iterator<Item = &Paint> {
|
||||
let runs = match &n.kind {
|
||||
NodeKind::Text(t) => t.runs.as_slice(),
|
||||
_ => &[],
|
||||
@@ -233,7 +233,7 @@ fn node_paints(n: &Node) -> impl Iterator<Item = &Paint> {
|
||||
}
|
||||
|
||||
/// Mutable [`node_paints`], in the same order.
|
||||
fn node_paints_mut(n: &mut Node) -> impl Iterator<Item = &mut Paint> {
|
||||
pub(crate) fn node_paints_mut(n: &mut Node) -> impl Iterator<Item = &mut Paint> {
|
||||
let runs = match &mut n.kind {
|
||||
NodeKind::Text(t) => t.runs.as_mut_slice(),
|
||||
_ => &mut [],
|
||||
|
||||
@@ -120,7 +120,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Make",
|
||||
["Object", "Image Trace"],
|
||||
None,
|
||||
"{id?: image or Image Trace group (default: selection), preset?: name (imageTrace.presets), 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), 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), 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, white is always its own colour, at most ten; 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 unless ignoreWhite makes them holes; fills/strokes are ignored; gradients and photos are refused)}, view?: as imageTrace.setView (default: the traced object's own, else tracingResult), ids?: [id…] (instead of id)} → {id, paths, anchors, colors}",
|
||||
"{id?: image or Image Trace group (default: selection), preset?: name (imageTrace.presets), 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), 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), 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, white is always its own colour, at most ten; 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 unless ignoreWhite makes them holes; fills/strokes are ignored; gradients and photos are refused)}, view?: as imageTrace.setView (default: the traced object's own, else tracingResult), ids?: [id…] (instead of id)} → {id, paths, anchors, colors}",
|
||||
has_selection_or_ids,
|
||||
|s, p| trace_make(s, p, false)
|
||||
),
|
||||
@@ -160,8 +160,8 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
has_selection_or_ids,
|
||||
trace_set_view
|
||||
),
|
||||
cmd!(query "imageTrace.presets", "Image Trace Presets", [], None, "{} → {presets: [{name, params}]}", always, |_, _| {
|
||||
Ok(json!({ "presets": tr::presets().into_iter().map(|(n, p)| json!({ "name": n, "params": p })).collect::<Vec<_>>() }))
|
||||
cmd!(query "imageTrace.presets", "Image Trace Presets", [], None, "{} → {presets: [{name, params, enhanced: the params of the preset's enhanced version, null if it has none}]}", always, |_, _| {
|
||||
Ok(json!({ "presets": tr::presets().into_iter().map(|(n, p)| json!({ "name": n, "params": p, "enhanced": tr::enhanced_preset(n) })).collect::<Vec<_>>() }))
|
||||
}),
|
||||
]
|
||||
}
|
||||
@@ -657,7 +657,10 @@ fn view_param(cmd: &str, p: &Value) -> Result<Option<TraceView>> {
|
||||
|
||||
fn trace_params(p: &Value) -> Result<tr::TraceParams> {
|
||||
let name = str_param(p, "preset").unwrap_or("Default");
|
||||
let base = tr::preset(name).ok_or_else(|| bad("imageTrace.make", format!("unknown preset `{name}` (see imageTrace.presets)")))?;
|
||||
let enhanced = p.get("enhanced").and_then(Value::as_bool).unwrap_or(false);
|
||||
// (A preset without an enhanced version is traced as it is.)
|
||||
let base = enhanced.then(|| tr::enhanced_preset(name)).flatten().or_else(|| tr::preset(name));
|
||||
let base = base.ok_or_else(|| bad("imageTrace.make", format!("unknown preset `{name}` (see imageTrace.presets)")))?;
|
||||
let Some(over) = p.get("params").filter(|v| !v.is_null()) else { return Ok(base) };
|
||||
let mut v = serde_json::to_value(&base).map_err(|e| EngineError::Other(e.to_string()))?;
|
||||
let (Some(o), Some(src)) = (v.as_object_mut(), over.as_object()) else { return Err(bad("imageTrace.make", "params must be an object")) };
|
||||
@@ -738,8 +741,9 @@ fn traced_gradient(g: &tr::TracedGradient, xf: vectorcraft_geom::Affine) -> Pain
|
||||
}
|
||||
|
||||
/// Auto Grouping: `nodes` (one per traced path, bottom-most first) with each path that has others
|
||||
/// inside it (`parents`, as the tracer gives them) in a group with them: the path, then what is
|
||||
/// inside it. Without parents, the nodes as they are.
|
||||
/// inside it (`parents`, as the tracer gives them) in a group with them, in the order they had:
|
||||
/// abutting paths painted in another order would show a seam where they meet. Without parents,
|
||||
/// the nodes as they are.
|
||||
fn grouped(d: &mut Document, nodes: Vec<Node>, parents: &[Option<usize>]) -> Vec<Arc<Node>> {
|
||||
let n = nodes.len();
|
||||
let mut inside: Vec<Vec<usize>> = vec![vec![]; n];
|
||||
@@ -761,8 +765,14 @@ fn grouped(d: &mut Document, nodes: Vec<Node>, parents: &[Option<usize>]) -> Vec
|
||||
let mut built: Vec<Option<Arc<Node>>> = vec![None; n];
|
||||
for &i in order.iter().rev() {
|
||||
let Some(node) = nodes.get_mut(i).and_then(Option::take) else { continue };
|
||||
let within: Vec<Arc<Node>> = inside.get(i).into_iter().flatten().filter_map(|&c| built.get_mut(c)?.take()).collect();
|
||||
let made = if within.is_empty() { node } else { Node::group(d.alloc_id(), std::iter::once(Arc::new(node)).chain(within).collect()) };
|
||||
let mut within: Vec<(usize, Arc<Node>)> = inside.get(i).into_iter().flatten().filter_map(|&c| Some((c, built.get_mut(c)?.take()?))).collect();
|
||||
let made = if within.is_empty() {
|
||||
node
|
||||
} else {
|
||||
within.push((i, Arc::new(node)));
|
||||
within.sort_by_key(|m| m.0);
|
||||
Node::group(d.alloc_id(), within.into_iter().map(|m| m.1).collect())
|
||||
};
|
||||
if let Some(slot) = built.get_mut(i) {
|
||||
*slot = Some(Arc::new(made));
|
||||
}
|
||||
@@ -778,12 +788,11 @@ fn trace_make(s: &mut Session, p: &Value, expand: bool) -> Result<Value> {
|
||||
let mut params = trace_params(p)?;
|
||||
let view = view_param(C, p)?;
|
||||
let params_json = serde_json::to_value(¶ms).map_err(|e| EngineError::Other(e.to_string()))?;
|
||||
// The preset's name if the parameters are exactly that preset's, else "Custom".
|
||||
// The preset's name if the parameters are exactly that preset's or its enhanced version's,
|
||||
// else "Custom".
|
||||
let named = str_param(p, "preset").unwrap_or("Default");
|
||||
let preset_label = match tr::preset(named) {
|
||||
Some(b) if serde_json::to_value(&b).ok().as_ref() == Some(¶ms_json) => named.to_string(),
|
||||
_ => "Custom".to_string(),
|
||||
};
|
||||
let is = |b: Option<tr::TraceParams>| b.and_then(|b| serde_json::to_value(&b).ok()).as_ref() == Some(¶ms_json);
|
||||
let preset_label = if is(tr::preset(named)) || is(tr::enhanced_preset(named)) { named.to_string() } else { "Custom".to_string() };
|
||||
let roots = root_ids(s, p)?;
|
||||
// The image (inside a trace group, or selected directly).
|
||||
let (target, img) = {
|
||||
|
||||
@@ -32,6 +32,7 @@ pub(super) fn import(name: &str, bytes: &[u8]) -> Result<Imported> {
|
||||
Ok(d) => {
|
||||
let mut b = Builder { doc: Document::new(0.0, 0.0), warnings: Vec::new(), k: 72.0 / d.dpi, layer: 0 };
|
||||
b.document(&d)?;
|
||||
b.doc.limit_depth_noting(&mut b.warnings);
|
||||
let mut warnings = d.warnings.iter().map(|w| format!("Affinity: not imported or approximated: {w}.")).collect::<Vec<_>>();
|
||||
warnings.extend(b.warnings.iter().map(|w| format!("Affinity: {w}.")));
|
||||
Ok(Imported { doc: b.doc, warnings, preview_only: false })
|
||||
|
||||
@@ -139,7 +139,8 @@ fn restore_or_import(
|
||||
/// (with a warning for each that can't be used).
|
||||
pub(crate) fn native_file(bytes: &[u8]) -> Result<(Document, vectorcraft_format::FileInfo, Vec<String>)> {
|
||||
let f = vectorcraft_format::load_file(bytes).map_err(err)?;
|
||||
let warnings = super::native::install_profiles(f.profiles);
|
||||
let mut warnings = super::native::install_profiles(f.profiles);
|
||||
warnings.extend(f.depth_notes);
|
||||
Ok((f.doc, f.info, warnings))
|
||||
}
|
||||
|
||||
|
||||
@@ -583,6 +583,10 @@ pub(crate) fn is_guide_path(d: &Document, id: NodeId) -> bool {
|
||||
d.node(id).is_some_and(|n| matches!(n.kind, NodeKind::Path { guide: true, .. }))
|
||||
}
|
||||
|
||||
/// The error of an edit that would nest objects deeper than [`vectorcraft_doc::MAX_DEPTH`].
|
||||
pub const TOO_DEEP: &str = "objects can't be nested more than 32 deep";
|
||||
const _: () = assert!(vectorcraft_doc::MAX_DEPTH == 32, "TOO_DEEP (and its catalog rows) names the cap");
|
||||
|
||||
/// Cheap sanity check after an edit: artboards and the objects just touched (the selection) must
|
||||
/// have finite, in-range geometry, so saved files always reload and renderers never see NaN/∞.
|
||||
fn doc_sane(d: &Document, sel: &Selection) -> bool {
|
||||
@@ -1245,6 +1249,9 @@ impl Session {
|
||||
}
|
||||
/// Add a document and make it active.
|
||||
pub fn add_document(&mut self, mut doc: Document, path: Option<String>) -> usize {
|
||||
// Every edit is checked against the document before it, so the first one is within the cap
|
||||
// (importers and the native reader have seen to theirs already, with a warning).
|
||||
doc.limit_depth();
|
||||
Self::refresh_text_bounds(&mut doc);
|
||||
// Guides from files saved before guides had layers go on a layer.
|
||||
doc.adopt_guides();
|
||||
@@ -1262,6 +1269,7 @@ impl Session {
|
||||
if index >= self.docs.len() {
|
||||
return false;
|
||||
}
|
||||
doc.limit_depth();
|
||||
Self::refresh_text_bounds(&mut doc);
|
||||
doc.adopt_guides();
|
||||
if self.active == Some(index) {
|
||||
@@ -1476,7 +1484,10 @@ impl Session {
|
||||
if !st.doc.variables.bindings.is_empty() {
|
||||
Arc::make_mut(&mut st.doc).prune_variable_bindings();
|
||||
}
|
||||
if doc_sane(&st.doc, &st.selection) {
|
||||
if st.doc.too_deep(Some(&before)) {
|
||||
// Deeper trees overflow the stack of what walks them.
|
||||
Err(EngineError::Other(TOO_DEEP.into()))
|
||||
} else if doc_sane(&st.doc, &st.selection) {
|
||||
Ok(v)
|
||||
} else {
|
||||
Err(EngineError::Other("result would exceed the canvas (coordinates out of range)".into()))
|
||||
|
||||
@@ -737,6 +737,18 @@ fn image_trace_auto_grouping_groups_what_is_inside() {
|
||||
(0..120).flat_map(|x| (0..80).map(move |y| (x, y))).map(|(x, y)| img.pixel(x, y)).collect::<Vec<_>>()
|
||||
};
|
||||
assert_eq!(shot(&s), shot(&flat));
|
||||
// In black and white too, where the white inside the ring is a path traced before the ring:
|
||||
// it stays under the ring in their group (over it, the ring's inner edge would show a seam).
|
||||
let bw = |grouping: bool| {
|
||||
let mut s = session();
|
||||
add_image(&mut s, &art, Affine::IDENTITY);
|
||||
let r = s.execute("imageTrace.make", &json!({"params": {"autoGrouping": grouping}})).unwrap();
|
||||
let groups = traced_paths(&node(&s, NodeId(r["id"].as_u64().unwrap()))).iter().filter(|n| matches!(n.kind, NodeKind::Group { .. })).count();
|
||||
(s, groups)
|
||||
};
|
||||
let (grouped, flat) = (bw(true), bw(false));
|
||||
assert!(grouped.1 > 0 && flat.1 == 0);
|
||||
assert_eq!(shot(&grouped.0), shot(&flat.0));
|
||||
// Traced again without the option, it is flat again; expanded, the groups stay.
|
||||
let r = s.execute("imageTrace.make", &json!({"preset": "6 Colors", "params": {"ignoreWhite": true}})).unwrap();
|
||||
assert_eq!(traced_paths(&node(&s, NodeId(r["id"].as_u64().unwrap()))).len(), 3);
|
||||
@@ -773,6 +785,29 @@ fn image_trace_gradients_fills_with_a_gradient() {
|
||||
assert!(flat.len() > 1 && flat.iter().all(|n| matches!(n.appearance.fill_paint(), Paint::Solid { .. })));
|
||||
}
|
||||
|
||||
/// A preset's enhanced version: the preset with Auto Grouping and Shapes, under the preset's name.
|
||||
#[test]
|
||||
fn image_trace_enhanced_presets() {
|
||||
let mut s = session();
|
||||
image_doc(&mut s);
|
||||
let r = s.execute("imageTrace.make", &json!({"preset": "6 Colors", "enhanced": true})).unwrap();
|
||||
let n = node(&s, NodeId(r["id"].as_u64().unwrap()));
|
||||
let stored = n.extras().trace.clone().unwrap();
|
||||
assert_eq!(stored["preset"], "6 Colors");
|
||||
assert_eq!((stored["params"]["shapes"].as_bool(), stored["params"]["autoGrouping"].as_bool()), (Some(true), Some(true)));
|
||||
// The disc of the test image is a live ellipse, in a group with nothing (it has nothing inside).
|
||||
let shapes: Vec<_> = traced_paths(&n).into_iter().filter(|c| matches!(&c.kind, NodeKind::Path { live: Some(_), .. })).collect();
|
||||
assert_eq!(shapes.len(), 1);
|
||||
// Listed with each preset; the legacy preset is as it was.
|
||||
let list = s.execute("imageTrace.presets", &json!({})).unwrap();
|
||||
let six = list["presets"].as_array().unwrap().iter().find(|p| p["name"] == "6 Colors").unwrap().clone();
|
||||
assert_eq!((six["params"]["shapes"].as_bool(), six["enhanced"]["shapes"].as_bool()), (Some(false), Some(true)));
|
||||
assert_eq!(list["presets"].as_array().unwrap().iter().find(|p| p["name"] == "High Fidelity Photo").unwrap()["enhanced"]["shapes"], false);
|
||||
assert!(s.execute("imageTrace.make", &json!({"preset": "No Such", "enhanced": true})).is_err());
|
||||
// Flat Logo, a preset of our own, has no enhanced version.
|
||||
assert!(list["presets"].as_array().unwrap().iter().find(|p| p["name"] == "Flat Logo").unwrap()["enhanced"].is_null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_trace_release_and_expand() {
|
||||
let mut s = session();
|
||||
|
||||
@@ -2440,3 +2440,345 @@ proptest! {
|
||||
checked("mutated script", json!({"dataBase64": vectorcraft_format::base64_encode(&bytes), "name": "sample.jsx"}))?;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- deep nesting ----------
|
||||
//
|
||||
// The tree is walked by recursion, so a file nested deeply enough overflowed the stack: an abort,
|
||||
// which nothing catches. Every reader caps what it reads, and no document is deeper than
|
||||
// `MAX_DEPTH`. These run on a thread with the stack of a Windows main thread (1 MB), where an
|
||||
// overflow ends the test run.
|
||||
|
||||
use vectorcraft_doc::MAX_DEPTH;
|
||||
|
||||
/// `f` on a thread with a 1 MB stack.
|
||||
fn on_a_small_stack(f: impl FnOnce() + Send + 'static) {
|
||||
std::thread::Builder::new().stack_size(1 << 20).spawn(f).unwrap().join().unwrap();
|
||||
}
|
||||
|
||||
/// Open `bytes` as the file `name`: an error, or a document within the cap that renders and
|
||||
/// exports. Returns the document and the import's warnings.
|
||||
fn open_deep(name: &str, bytes: &[u8]) -> Option<(Document, Vec<String>)> {
|
||||
let loaded = vectorcraft_engine::cmd::fileio::load(name, bytes).ok()?;
|
||||
assert!(!loaded.doc.too_deep(None), "{name}: deeper than the cap");
|
||||
let doc = loaded.doc.clone();
|
||||
survive(name, move || Some(doc)).unwrap();
|
||||
Some((loaded.doc, loaded.warnings))
|
||||
}
|
||||
|
||||
fn merged(warnings: &[String]) -> bool {
|
||||
warnings.iter().any(|w| w.contains("nested more than"))
|
||||
}
|
||||
|
||||
fn paths_in(doc: &Document) -> usize {
|
||||
let mut paths = 0;
|
||||
doc.walk(|n| paths += usize::from(n.path_data().is_some()));
|
||||
paths
|
||||
}
|
||||
|
||||
const SVG_NS: &str = "xmlns=\"http://www.w3.org/2000/svg\"";
|
||||
|
||||
/// `n` groups in one another, each named (so it is kept) and with a rectangle beside the next.
|
||||
fn nested_svg(n: usize) -> String {
|
||||
let open: String = (0..n).map(|i| format!("<g id=\"g{i}\"><rect width=\"5\" height=\"5\"/>")).collect();
|
||||
format!("<svg {SVG_NS} width=\"100\" height=\"100\">{open}<rect width=\"10\" height=\"10\"/>{}</svg>", "</g>".repeat(n))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deeply_nested_svg_is_refused_or_merged() {
|
||||
on_a_small_stack(|| {
|
||||
// Within what the reader takes: opened, the groups past the cap merged into the last.
|
||||
let (doc, warnings) = open_deep("deep.svg", nested_svg(40).as_bytes()).expect("40 groups open");
|
||||
assert!(merged(&warnings), "{warnings:?}");
|
||||
assert_eq!(paths_in(&doc), 41, "no art is lost");
|
||||
let (_, warnings) = open_deep("shallow.svg", nested_svg(20).as_bytes()).expect("20 groups open");
|
||||
assert!(!merged(&warnings), "{warnings:?}");
|
||||
// As deep as the reader goes (on a stack of its own): opened and merged the same.
|
||||
let most = vectorcraft_svg::MAX_ELEMENT_DEPTH;
|
||||
let (doc, warnings) = open_deep("deepest.svg", nested_svg(most - 2).as_bytes()).expect("opens at the reader's cap");
|
||||
assert!(merged(&warnings), "{warnings:?}");
|
||||
assert_eq!(paths_in(&doc), most - 1, "no art is lost");
|
||||
// Past it: an error that says why, whatever the depth.
|
||||
for n in [most, 2 * most, 200_000] {
|
||||
let e = vectorcraft_engine::cmd::fileio::load("deep.svg", nested_svg(n).as_bytes()).err().expect("refused").to_string();
|
||||
assert!(e.contains(&format!("nested more than {most} deep")), "{n}: {e}");
|
||||
}
|
||||
// Unnamed groups of one object aren't kept, but usvg walks them all the same.
|
||||
let plain = format!(
|
||||
"<svg {SVG_NS} width=\"9\" height=\"9\">{}<rect width=\"1\" height=\"1\"/>{}</svg>",
|
||||
"<g>".repeat(2 * most),
|
||||
"</g>".repeat(2 * most)
|
||||
);
|
||||
assert!(open_deep("plain.svg", plain.as_bytes()).is_none());
|
||||
// A chain of `<use>`: shallow XML, a deep tree.
|
||||
let links: String = (0..3000).map(|i| format!("<g id=\"u{i}\"><use href=\"#u{}\"/></g>", i + 1)).collect();
|
||||
let chain = format!(
|
||||
"<svg {SVG_NS} width=\"9\" height=\"9\"><defs>{links}<rect id=\"u3000\" width=\"1\" height=\"1\"/></defs><use href=\"#u0\"/></svg>"
|
||||
);
|
||||
assert!(open_deep("chain.svg", chain.as_bytes()).is_none());
|
||||
// Placed and pasted, as opened.
|
||||
let mut s = vectorcraft_engine::Session::new();
|
||||
s.execute("file.new", &json!({"width": 300, "height": 200})).unwrap();
|
||||
for n in [40, 2 * most] {
|
||||
let p = json!({"name": "deep.svg", "dataBase64": vectorcraft_format::base64_encode(nested_svg(n).as_bytes())});
|
||||
let _ = no_panic(s.execute("file.place", &p));
|
||||
let _ = no_panic(s.execute("clipboard.importSvg", &json!({"svg": nested_svg(n)})));
|
||||
let _ = no_panic(s.execute("edit.paste", &json!({})));
|
||||
assert!(!s.doc().unwrap().doc.too_deep(None));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// A page whose content is `content`, with `resources`.
|
||||
fn deep_pdf(content: &str, resources: &str) -> Vec<u8> {
|
||||
handmade_pdf(content, resources, "[0 0 100 100]")
|
||||
}
|
||||
|
||||
/// An EPS whose program is `body`.
|
||||
fn deep_eps(body: &str) -> Vec<u8> {
|
||||
format!("%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: 0 0 100 100\n%%EndComments\n{body}\nshowpage\n%%EOF\n").into_bytes()
|
||||
}
|
||||
|
||||
/// A test for each file: it opens as an error or as a document within the cap, on a small stack.
|
||||
macro_rules! deep_files {
|
||||
($($test:ident: $name:literal, $bytes:expr;)*) => {$(
|
||||
#[test]
|
||||
fn $test() {
|
||||
on_a_small_stack(|| {
|
||||
open_deep($name, &$bytes);
|
||||
});
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
deep_files! {
|
||||
// PDF: clips inside clips (saved states never restored); arrays in arrays as an operand and as
|
||||
// an object; dictionaries in dictionaries as a resource and inline; marked content never ended.
|
||||
pdf_clips_in_clips: "clips.pdf", deep_pdf(&format!("{} 0 0 5 5 re f", "q 0 0 50 50 re W n ".repeat(20_000)), "<< >>");
|
||||
pdf_arrays_in_an_operand: "arrays.pdf", deep_pdf(&format!("BT {}{} TJ ET 0 0 5 5 re f", "[".repeat(100_000), "]".repeat(100_000)), "<< >>");
|
||||
pdf_arrays_in_an_object: "boxes.pdf", handmade_pdf("0 0 5 5 re f", "<< >>", &format!("{}0 0 100 100{}", "[".repeat(100_000), "]".repeat(100_000)));
|
||||
pdf_dictionaries_in_resources: "dicts.pdf", deep_pdf("0 0 5 5 re f", &format!("{}{}", "<< /A ".repeat(50_000), ">> ".repeat(50_000)));
|
||||
pdf_dictionaries_in_content: "inline.pdf", deep_pdf(&format!("/P {}{} DP 0 0 5 5 re f", "<< /A ".repeat(50_000), ">> ".repeat(50_000)), "<< >>");
|
||||
pdf_marked_content_never_ended: "marked.pdf", deep_pdf(&format!("{} 0 0 5 5 re f", "/Span << /MCID 0 >> BDC ".repeat(50_000)), "<< >>");
|
||||
// PostScript: clips inside clips; procedures in procedures; arrays in arrays; a procedure that
|
||||
// calls itself; dictionaries never ended.
|
||||
eps_clips_in_clips: "clips.eps", deep_eps(&format!("{} 0 0 5 5 rectfill", "gsave 0 0 50 50 rectclip ".repeat(20_000)));
|
||||
eps_procedures_in_procedures: "procs.eps", deep_eps(&format!("{}{} 0 0 5 5 rectfill", "{".repeat(100_000), "}".repeat(100_000)));
|
||||
eps_arrays_in_arrays: "arrays.eps", deep_eps(&format!("{}{} pop 0 0 5 5 rectfill", "[".repeat(100_000), "]".repeat(100_000)));
|
||||
eps_a_procedure_calling_itself: "runaway.eps", deep_eps("/f { 0 0 5 5 rectfill f } def f");
|
||||
eps_dictionaries_never_ended: "dicts.eps", deep_eps(&format!("{} 0 0 5 5 rectfill", "1 dict begin ".repeat(100_000)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deeply_nested_dxf_blocks_are_read_within_the_cap() {
|
||||
on_a_small_stack(|| {
|
||||
// Block `B0` inserts `B1`, which inserts `B2`, and so on; the last one holds a line.
|
||||
let pair = |c: i32, v: &str| (c, v.to_string());
|
||||
let mut pairs = vec![pair(0, "SECTION"), pair(2, "BLOCKS")];
|
||||
for i in 0..2_000 {
|
||||
pairs.extend([pair(0, "BLOCK"), pair(2, &format!("B{i}")), pair(10, "0"), pair(20, "0")]);
|
||||
if i + 1 < 2_000 {
|
||||
pairs.extend([pair(0, "INSERT"), pair(2, &format!("B{}", i + 1)), pair(10, "1"), pair(20, "1")]);
|
||||
} else {
|
||||
pairs.extend([pair(0, "LINE"), pair(10, "0"), pair(20, "0"), pair(11, "5"), pair(21, "5")]);
|
||||
}
|
||||
pairs.push(pair(0, "ENDBLK"));
|
||||
}
|
||||
pairs.extend([pair(0, "ENDSEC"), pair(0, "SECTION"), pair(2, "ENTITIES"), pair(0, "INSERT"), pair(2, "B0"), pair(10, "0"), pair(20, "0")]);
|
||||
pairs.extend([pair(0, "LINE"), pair(10, "0"), pair(20, "0"), pair(11, "9"), pair(21, "9"), pair(0, "ENDSEC"), pair(0, "EOF")]);
|
||||
open_deep("blocks.dxf", dxf_text(&pairs).as_bytes()).expect("opens");
|
||||
});
|
||||
}
|
||||
|
||||
/// A layer holding `levels` groups in one another around a rectangle, each with a second
|
||||
/// rectangle beside the next: `levels + 2` deep.
|
||||
fn nested_document(levels: usize) -> Document {
|
||||
use vectorcraft_doc::Node;
|
||||
use vectorcraft_testkit::fixtures::DocBuilder;
|
||||
let mut b = DocBuilder::new(100.0, 100.0);
|
||||
let rect =
|
||||
|b: &mut DocBuilder, x: f64| b.rect_node(vectorcraft_geom::Rect::new(x, 0.0, x + 10.0, 10.0), vectorcraft_color::Color::rgb8(200, 0, 0));
|
||||
let mut inner = rect(&mut b, 0.0);
|
||||
for _ in 0..levels {
|
||||
let beside = rect(&mut b, 20.0);
|
||||
inner = Node::group(b.alloc(), vec![std::sync::Arc::new(inner), std::sync::Arc::new(beside)]);
|
||||
inner.opacity = 0.9;
|
||||
}
|
||||
b.add(inner);
|
||||
b.build()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_document_at_the_cap_saves_opens_and_exports() {
|
||||
on_a_small_stack(|| {
|
||||
let doc = nested_document(MAX_DEPTH - 2);
|
||||
assert!(!doc.too_deep(None));
|
||||
let back = vectorcraft_format::load_file(&vectorcraft_format::save_file(&doc)).expect("what can be made opens again");
|
||||
assert!(back.depth_notes.is_empty());
|
||||
assert_eq!(back.doc.layers, doc.layers);
|
||||
survive("at the cap", || Some(doc.clone())).unwrap();
|
||||
// Every format that keeps the nesting reads its own file back within the cap.
|
||||
let mut s = vectorcraft_engine::Session::new();
|
||||
s.add_document(doc, None);
|
||||
for format in ["svg", "pdf", "eps", "ai"] {
|
||||
let r = s.execute("document.export", &json!({"format": format})).unwrap();
|
||||
let bytes = vectorcraft_format::base64_decode(r["dataBase64"].as_str().unwrap()).unwrap();
|
||||
open_deep(&format!("cap.{format}"), &bytes).unwrap_or_else(|| panic!("{format} opens"));
|
||||
}
|
||||
for cmd in ["select.all", "document.inspect", "document.info", "edit.copy", "edit.paste", "object.ungroup", "edit.undo"] {
|
||||
no_panic(s.execute(cmd, &json!({}))).unwrap_or_else(|e| panic!("{cmd}: {e}"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_native_file_nested_too_deep_is_merged_or_refused() {
|
||||
on_a_small_stack(|| {
|
||||
// Saved before the cap by a version that could still open it: opened, the deepest merged.
|
||||
let old = vectorcraft_format::save_file(&nested_document(MAX_DEPTH + 2));
|
||||
let (doc, warnings) = open_deep("old.vectorcraft", &old).expect("opens");
|
||||
assert!(merged(&warnings), "{warnings:?}");
|
||||
assert_eq!(paths_in(&doc), MAX_DEPTH + 3, "no art is lost");
|
||||
// Deeper than any version could open, and a hostile file: an error that says why. Written
|
||||
// by hand: a tree this deep can't be built and saved.
|
||||
let text = String::from_utf8(vectorcraft_format::save(&nested_document(0), false)).unwrap();
|
||||
let (open, close) = ("{\"id\":900000,\"kind\":{\"type\":\"group\",\"children\":[", "]}}");
|
||||
let at = text.find("\"children\":[").expect("the layer's children") + "\"children\":[".len();
|
||||
for levels in [100, 50_000] {
|
||||
let deep = format!("{}{}{},{}", &text[..at], open.repeat(levels), close.repeat(levels), &text[at..]);
|
||||
let e = vectorcraft_engine::cmd::fileio::load("deep.vectorcraft", deep.as_bytes()).err().expect("refused").to_string();
|
||||
assert!(e.contains("nested too deep"), "{levels}: {e}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commands_cannot_nest_deeper_than_the_cap() {
|
||||
on_a_small_stack(|| {
|
||||
let mut s = vectorcraft_engine::Session::new();
|
||||
s.execute("file.new", &json!({"width": 200, "height": 200})).unwrap();
|
||||
s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 50, "height": 50})).unwrap();
|
||||
s.execute("select.all", &json!({})).unwrap();
|
||||
let mut grouped = 0;
|
||||
for _ in 0..10_000 {
|
||||
match no_panic(s.execute("object.group", &json!({}))) {
|
||||
Ok(_) => grouped += 1,
|
||||
Err(e) => assert!(e.to_string().contains(vectorcraft_engine::TOO_DEEP), "after {grouped} groups: {e}"),
|
||||
}
|
||||
}
|
||||
// The layer, the groups and the rectangle.
|
||||
assert_eq!(grouped, MAX_DEPTH - 2);
|
||||
assert!(!s.doc().unwrap().doc.too_deep(None));
|
||||
// Other ways down: a clipping mask, a copy pasted, a symbol made of it.
|
||||
for (cmd, p) in [
|
||||
("object.clippingMask.make", json!({})),
|
||||
("edit.copy", json!({})),
|
||||
("edit.pasteInFront", json!({})),
|
||||
("symbol.new", json!({"name": "deep"})),
|
||||
] {
|
||||
let _ = no_panic(s.execute(cmd, &p));
|
||||
assert!(!s.doc().unwrap().doc.too_deep(None), "{cmd}");
|
||||
}
|
||||
survive("grouped to the cap", || Some((*s.doc().unwrap().doc).clone())).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
/// A document whose one object is an instance of the first of `count` symbols: each symbol's art
|
||||
/// is `levels` groups around an instance of the next symbol, the last one of `last`.
|
||||
fn symbols_document(count: usize, levels: usize, last: &str) -> Document {
|
||||
use vectorcraft_doc::{Node, NodeKind, Symbol};
|
||||
let instance =
|
||||
|doc: &mut Document, of: String| Node::new(doc.alloc_id(), NodeKind::SymbolInstance { symbol: of, xf: vectorcraft_geom::Affine::IDENTITY });
|
||||
let mut doc = nested_document(1);
|
||||
for i in 0..count {
|
||||
let mut art = instance(&mut doc, if i + 1 < count { format!("s{}", i + 1) } else { last.to_string() });
|
||||
for _ in 0..levels {
|
||||
art = Node::group(doc.alloc_id(), vec![std::sync::Arc::new(art)]);
|
||||
}
|
||||
doc.symbols.push(Symbol { name: format!("s{i}"), art: std::sync::Arc::new(art) });
|
||||
}
|
||||
let first = instance(&mut doc, "s0".into());
|
||||
let layer = doc.layers[0].id;
|
||||
doc.insert(Some(layer), usize::MAX, first).unwrap();
|
||||
doc
|
||||
}
|
||||
|
||||
/// `doc` saved (as an older version, or a hostile file, could have) and opened again: within the
|
||||
/// caps (`kept`: as it was), drawn, exported and edited.
|
||||
fn symbols_survive(what: &str, doc: Document, kept: bool) {
|
||||
let (back, warnings) = open_deep("symbols.vectorcraft", &vectorcraft_format::save_file(&doc)).expect("opens");
|
||||
assert_eq!(!warnings.iter().any(|w| w.contains("symbol instances")), kept, "{what}: {warnings:?}");
|
||||
let mut s = vectorcraft_engine::Session::new();
|
||||
s.add_document(back, None);
|
||||
for cmd in [
|
||||
"select.all",
|
||||
"document.inspect",
|
||||
"document.info",
|
||||
"object.expand",
|
||||
"symbol.breakLink",
|
||||
"object.flattenTransparency",
|
||||
"edit.copy",
|
||||
"edit.paste",
|
||||
] {
|
||||
let _ = no_panic(s.execute(cmd, &json!({})));
|
||||
assert!(!s.doc().unwrap().doc.too_deep(None), "{what}: {cmd}");
|
||||
}
|
||||
for format in ["svg", "pdf", "eps", "png"] {
|
||||
let _ = no_panic(s.execute("document.export", &json!({"format": format})));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_symbol_holding_itself_opens_without_the_instance() {
|
||||
on_a_small_stack(|| {
|
||||
symbols_survive("a symbol holding itself", symbols_document(1, 1, "s0"), false);
|
||||
symbols_survive("symbols holding one another", symbols_document(3, 2, "s0"), false);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbols_in_symbols_are_drawn_only_so_deep() {
|
||||
on_a_small_stack(|| {
|
||||
// As deep as symbols draw: each one's art as deep as art goes.
|
||||
symbols_survive("symbols at the cap", symbols_document(3, MAX_DEPTH - 2, "none"), true);
|
||||
symbols_survive("symbols in symbols", symbols_document(40, MAX_DEPTH - 1, "none"), false);
|
||||
// A symbol can't be made to hold itself, or one that holds it, by editing either.
|
||||
let mut s = vectorcraft_engine::Session::new();
|
||||
s.add_document(symbols_document(2, 1, "none"), None);
|
||||
s.execute("select.all", &json!({})).unwrap();
|
||||
for (cmd, p) in [
|
||||
("symbol.new", json!({"name": "s1"})),
|
||||
("symbol.update", json!({"name": "s1"})),
|
||||
("symbol.update", json!({"name": "s0"})),
|
||||
("symbol.replace", json!({"name": "s1"})),
|
||||
] {
|
||||
let _ = no_panic(s.execute(cmd, &p));
|
||||
assert!(!s.doc().unwrap().doc.too_deep(None), "{cmd} {p}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_pattern_painting_its_own_tile_opens_without_the_paint() {
|
||||
on_a_small_stack(|| {
|
||||
use vectorcraft_color::Paint;
|
||||
use vectorcraft_doc::{Appearance, PatternDef};
|
||||
let painted = |doc: &mut Document, with: &str| {
|
||||
let fill = Paint::Pattern { pattern: with.into(), xf: vectorcraft_geom::Affine::IDENTITY };
|
||||
let square = vectorcraft_geom::shapes::rectangle(vectorcraft_geom::Rect::new(0.0, 0.0, 20.0, 20.0));
|
||||
vectorcraft_doc::Node::path(doc.alloc_id(), square, Appearance::basic(fill, Paint::None, 0.0))
|
||||
};
|
||||
// One pattern painting its own tile, and two painting each other's.
|
||||
for names in [["p", "p"], ["p", "q"]] {
|
||||
let mut doc = nested_document(1);
|
||||
let (first, second) = (painted(&mut doc, names[1]), painted(&mut doc, names[0]));
|
||||
doc.patterns.push(PatternDef::new(names[0], vec![std::sync::Arc::new(first)]));
|
||||
if names[0] != names[1] {
|
||||
doc.patterns.push(PatternDef::new(names[1], vec![std::sync::Arc::new(second)]));
|
||||
}
|
||||
let on_layer = painted(&mut doc, "p");
|
||||
let layer = doc.layers[0].id;
|
||||
doc.insert(Some(layer), usize::MAX, on_layer).unwrap();
|
||||
symbols_survive("a pattern painting its own tile", doc, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
//! Every operation counts against a budget and procedures nest only so deep, so a looping or
|
||||
//! recursing program ends with [`PsError::Limit`] instead of hanging.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use vectorcraft_geom::Affine;
|
||||
@@ -86,7 +85,7 @@ struct SaveMark {
|
||||
}
|
||||
|
||||
fn new_dict() -> DictRef {
|
||||
Rc::new(RefCell::new(Dict::new()))
|
||||
DictRef::new(Dict::new())
|
||||
}
|
||||
|
||||
/// An encoding vector: 256 `/.notdef` names (type is read as Latin-1 whatever the encoding).
|
||||
|
||||
@@ -268,5 +268,7 @@ fn finish_with(out: Out, layers: Vec<Arc<Node>>) -> Imported {
|
||||
}
|
||||
}
|
||||
doc.layers = layers;
|
||||
Imported { document: doc, warnings: out.warnings, preview: false }
|
||||
let mut warnings = out.warnings;
|
||||
doc.limit_depth_noting(&mut warnings);
|
||||
Imported { document: doc, warnings, preview: false }
|
||||
}
|
||||
@@ -993,6 +993,7 @@ fn build(data: &[u8], visible: Option<&Document>, page: Page, outlined: bool, tr
|
||||
}
|
||||
None => unmark(&mut doc.layers, true, &mut vec![]),
|
||||
}
|
||||
doc.limit_depth_noting(&mut notes);
|
||||
Ok((doc, notes))
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,73 @@ use super::graphics::GState;
|
||||
pub(crate) type Dict = HashMap<Key, Obj>;
|
||||
|
||||
/// A shared, mutable dictionary.
|
||||
pub(crate) type DictRef = Rc<RefCell<Dict>>;
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct DictRef(Rc<RefCell<Dict>>);
|
||||
|
||||
impl DictRef {
|
||||
pub fn new(d: Dict) -> Self {
|
||||
Self(Rc::new(RefCell::new(d)))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for DictRef {
|
||||
type Target = Rc<RefCell<Dict>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DictRef {
|
||||
fn drop(&mut self) {
|
||||
if Rc::strong_count(&self.0) == 1
|
||||
&& let Ok(mut d) = self.0.try_borrow_mut()
|
||||
{
|
||||
drop_held(d.drain().map(|(_, v)| v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// What the composite objects being dropped held, waiting for their turn (while any is).
|
||||
static HELD: RefCell<Option<Vec<Obj>>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
/// Drop what a composite object held, now that nothing else refers to it, without recursion: a
|
||||
/// program nests arrays and dictionaries as deep as it likes (`[ exch ]` in a loop), and dropping
|
||||
/// them one inside the other overflows the stack. What `held` holds in its turn waits in [`HELD`]
|
||||
/// for the outermost drop, which works through it.
|
||||
fn drop_held(held: impl Iterator<Item = Obj>) {
|
||||
let holds = |o: &Obj| matches!(o, Obj::Array { .. } | Obj::Dict(_) | Obj::File { .. } | Obj::GState(_));
|
||||
// (Without the list, when the thread is ending: dropped as they come.)
|
||||
let outermost = HELD.try_with(|h| {
|
||||
let Ok(mut h) = h.try_borrow_mut() else { return false };
|
||||
let outermost = h.is_none();
|
||||
h.get_or_insert_with(Vec::new).extend(held.filter(holds));
|
||||
outermost
|
||||
});
|
||||
if !matches!(outermost, Ok(true)) {
|
||||
return;
|
||||
}
|
||||
// Each drop here puts what it held on the list instead of dropping it.
|
||||
while let Ok(Some(next)) = HELD.try_with(|h| h.try_borrow_mut().ok().and_then(|mut h| h.as_mut().and_then(Vec::pop))) {
|
||||
drop(next);
|
||||
}
|
||||
let _ = HELD.try_with(|h| h.try_borrow_mut().map(|mut h| *h = None)); // Nothing to do when the thread is ending.
|
||||
}
|
||||
|
||||
/// What [`Shared`] values may be: bytes, or objects (which may hold more of themselves).
|
||||
pub(crate) trait Held: Sized {
|
||||
/// Drop the values of storage nothing refers to any more.
|
||||
fn release(_values: Vec<Self>) {}
|
||||
}
|
||||
|
||||
impl Held for u8 {}
|
||||
|
||||
impl Held for Obj {
|
||||
fn release(values: Vec<Self>) {
|
||||
drop_held(values.into_iter());
|
||||
}
|
||||
}
|
||||
|
||||
/// A dictionary key.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
@@ -72,7 +138,7 @@ impl Obj {
|
||||
}
|
||||
|
||||
pub fn dict(d: Dict) -> Self {
|
||||
Self::Dict(Rc::new(RefCell::new(d)))
|
||||
Self::Dict(DictRef::new(d))
|
||||
}
|
||||
|
||||
/// The number this is (integers and reals).
|
||||
@@ -135,13 +201,23 @@ impl Obj {
|
||||
/// The values of a string or an array: a run of storage that other strings or arrays may share
|
||||
/// (`getinterval` makes a new object over part of the same values, as PostScript does).
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Shared<T> {
|
||||
pub(crate) struct Shared<T: Held> {
|
||||
buf: Rc<RefCell<Vec<T>>>,
|
||||
at: usize,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<T: Clone> Shared<T> {
|
||||
impl<T: Held> Drop for Shared<T> {
|
||||
fn drop(&mut self) {
|
||||
if Rc::strong_count(&self.buf) == 1
|
||||
&& let Ok(mut values) = self.buf.try_borrow_mut()
|
||||
{
|
||||
T::release(std::mem::take(&mut *values));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Held> Shared<T> {
|
||||
pub fn new(v: Vec<T>) -> Self {
|
||||
Self { len: v.len(), buf: Rc::new(RefCell::new(v)), at: 0 }
|
||||
}
|
||||
@@ -198,7 +274,7 @@ impl<T: Clone> Shared<T> {
|
||||
}
|
||||
|
||||
/// Hashes what [`Shared::same`] compares: the storage and the run of it.
|
||||
impl<T> Hash for Shared<T> {
|
||||
impl<T: Held> Hash for Shared<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
Rc::as_ptr(&self.buf).hash(state);
|
||||
(self.at, self.len).hash(state);
|
||||
|
||||
@@ -389,7 +389,7 @@ impl Interp<'_> {
|
||||
d.insert(Key::name("FontMatrix"), matrix_obj(Affine::scale(0.001)));
|
||||
d.insert(Key::name("FontBBox"), Obj::array(vec![Obj::Int(0), Obj::Int(-250), Obj::Int(1000), Obj::Int(1000)]));
|
||||
d.insert(Key::name("Encoding"), Obj::array(vec![Obj::name(".notdef"); 256]));
|
||||
let font = Rc::new(std::cell::RefCell::new(d));
|
||||
let font = DictRef::new(d);
|
||||
self.fonts.borrow_mut().insert(key, Obj::Dict(font.clone()));
|
||||
Ok(font)
|
||||
}
|
||||
@@ -489,7 +489,7 @@ fn transformed(font: &DictRef, m: Affine) -> DictRef {
|
||||
let mut d = font.borrow().clone();
|
||||
let fm = d.get(&Key::name("FontMatrix")).and_then(|o| o.items().and_then(|i| matrix_of(&i.borrow()))).unwrap_or(Affine::scale(0.001));
|
||||
d.insert(Key::name("FontMatrix"), matrix_obj(m * fm));
|
||||
Rc::new(std::cell::RefCell::new(d))
|
||||
DictRef::new(d)
|
||||
}
|
||||
|
||||
/// The character a glyph name stands for (`A`, `space`, `uni20AC`), else a bullet.
|
||||
|
||||
@@ -482,6 +482,9 @@ pub fn load(bytes: &[u8]) -> Result<Document, FormatError> {
|
||||
#[derive(Debug)]
|
||||
pub struct NativeFile {
|
||||
pub doc: Document,
|
||||
/// What was done to bring the document within the nesting caps
|
||||
/// ([`Document::limit_depth_noting`]: a file saved before them, or a damaged one).
|
||||
pub depth_notes: Vec<String>,
|
||||
pub info: FileInfo,
|
||||
/// ICC files by profile name (saved with Embed ICC Profiles; undecodable ones are left out).
|
||||
pub profiles: BTreeMap<String, Vec<u8>>,
|
||||
@@ -511,7 +514,11 @@ pub fn load_info(bytes: &[u8]) -> Result<(Document, FileInfo), FormatError> {
|
||||
/// [`load`], with everything else the file carries.
|
||||
pub fn load_file(bytes: &[u8]) -> Result<NativeFile, FormatError> {
|
||||
let file = unpack::Unpacked::new(bytes)?;
|
||||
let f: File = serde_json::from_slice(file.json()).map_err(|e| FormatError::NotVectorcraft(e.to_string()))?;
|
||||
// The JSON reader stops at 128 levels (a document's objects use three each).
|
||||
let f: File = serde_json::from_slice(file.json()).map_err(|e| {
|
||||
let why = if e.to_string().contains("recursion limit") { format!("its objects are nested too deep ({e})") } else { e.to_string() };
|
||||
FormatError::NotVectorcraft(why)
|
||||
})?;
|
||||
if f.format != EXTENSION && f.format != LEGACY_EXTENSION {
|
||||
return Err(FormatError::NotVectorcraft(format!("format is `{}`", f.format)));
|
||||
}
|
||||
@@ -545,8 +552,10 @@ pub fn load_file(bytes: &[u8]) -> Result<NativeFile, FormatError> {
|
||||
// Untrusted: capped, named and pointing at objects the file has.
|
||||
doc.tidy_saved_selections();
|
||||
doc.tidy_graph_designs();
|
||||
let mut depth_notes = vec![];
|
||||
doc.limit_depth_noting(&mut depth_notes);
|
||||
let profiles = f.profiles.into_iter().filter_map(|(name, icc)| Some((name, bytes_of(&icc)?))).collect();
|
||||
Ok(NativeFile { doc, info: FileInfo { version: f.version, legacy: f.format == LEGACY_EXTENSION }, profiles })
|
||||
Ok(NativeFile { doc, depth_notes, info: FileInfo { version: f.version, legacy: f.format == LEGACY_EXTENSION }, profiles })
|
||||
}
|
||||
|
||||
/// The members of a native file besides its document (which is skipped, not decoded).
|
||||
|
||||
@@ -102,7 +102,9 @@ pub struct Imported {
|
||||
/// Read an EMF or WMF file (with or without a placeable header) into a document whose one
|
||||
/// artboard is the picture's frame.
|
||||
pub fn import(bytes: &[u8]) -> Result<Imported, String> {
|
||||
import::import(bytes)
|
||||
let mut i = import::import(bytes)?;
|
||||
i.document.limit_depth_noting(&mut i.warnings);
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -149,6 +149,7 @@ pub const PAST_PAGE_NOTE: &str = "art reaching past the page was clipped to it,
|
||||
pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportReport, PdfError> {
|
||||
let original = crate::pages::open(bytes, opts.password.as_deref())?;
|
||||
let picked = crate::pages::picked(opts, original.pages().len())?;
|
||||
picked.iter().filter_map(|&n| original.pages().get(n)).try_for_each(crate::pages::content_too_deep)?;
|
||||
let mut ocgs = Ocgs::read(&original);
|
||||
let route = opts.layers && !ocgs.list.is_empty();
|
||||
// Content that is off imports as hidden layers: an update of the file turns every group on
|
||||
@@ -166,6 +167,7 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
|
||||
let all_on = turned_on.is_some();
|
||||
let pdf = turned_on.unwrap_or(original);
|
||||
let pages = pdf.pages();
|
||||
picked.iter().filter_map(|&n| pages.get(n)).try_for_each(crate::pages::content_too_deep)?;
|
||||
|
||||
let sink: Arc<Mutex<Vec<String>>> = Arc::default();
|
||||
let sink2 = sink.clone();
|
||||
@@ -417,6 +419,7 @@ pub fn import_with_report(bytes: &[u8], opts: &ImportOptions) -> Result<ImportRe
|
||||
}
|
||||
}
|
||||
}
|
||||
doc.limit_depth_noting(&mut warnings);
|
||||
Ok(ImportReport { document: doc, warnings, native: crate::editing::editing_in(&pdf), only_text })
|
||||
}
|
||||
|
||||
|
||||
@@ -33,12 +33,76 @@ pub fn is_postscript(bytes: &[u8]) -> bool {
|
||||
bytes.starts_with(b"%!PS") || bytes.starts_with(&[0xC5, 0xD0, 0xD3, 0xC6])
|
||||
}
|
||||
|
||||
/// The deepest a file's arrays and dictionaries may nest. The PDF reader (hayro) reads them by
|
||||
/// recursion, without a cap of its own: a file nested deeply enough overflows the stack, which is
|
||||
/// an abort, not an error. Far beyond what a PDF writer makes.
|
||||
const MAX_OBJECT_NESTING: usize = 256;
|
||||
|
||||
fn too_deep() -> PdfError {
|
||||
PdfError::Parse(format!("its arrays and dictionaries are nested more than {MAX_OBJECT_NESTING} deep"))
|
||||
}
|
||||
|
||||
/// Do the arrays and dictionaries in the text of `pdf` nest deeper than [`MAX_OBJECT_NESTING`]?
|
||||
/// Read as tokens (strings and comments hold none), an object at a time. The data of a stream
|
||||
/// with a filter is skipped: it isn't PDF text until decoded, which this doesn't do, so nesting
|
||||
/// inside compressed streams is not seen here.
|
||||
fn nests_too_deep(pdf: &[u8]) -> bool {
|
||||
let after =
|
||||
|from: usize, what: &[u8]| pdf.get(from..).and_then(|rest| rest.windows(what.len()).position(|w| w == what)).map(|at| from + at + what.len());
|
||||
let word = |at: usize, w: &[u8]| pdf.get(at..).is_some_and(|rest| rest.starts_with(w));
|
||||
let (mut depth, mut filtered) = (0usize, false);
|
||||
let mut i = 0;
|
||||
while let Some(&c) = pdf.get(i) {
|
||||
i += 1;
|
||||
match c {
|
||||
b'%' => i += pdf.get(i..).and_then(|rest| rest.iter().position(|c| matches!(c, b'\n' | b'\r'))).unwrap_or(pdf.len()),
|
||||
b'(' => {
|
||||
// A string: parentheses nest in it, a backslash takes the next byte.
|
||||
let mut open = 1usize;
|
||||
while open > 0 {
|
||||
match pdf.get(i) {
|
||||
None => return false,
|
||||
Some(b'\\') => i += 1,
|
||||
Some(b'(') => open += 1,
|
||||
Some(b')') => open -= 1,
|
||||
Some(_) => {}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
b'[' => depth += 1,
|
||||
b']' => depth = depth.saturating_sub(1),
|
||||
b'<' if pdf.get(i) == Some(&b'<') => (depth, i) = (depth + 1, i + 1),
|
||||
b'>' if pdf.get(i) == Some(&b'>') => (depth, i) = (depth.saturating_sub(1), i + 1),
|
||||
// A string in hexadecimal.
|
||||
b'<' => i = after(i, b">").unwrap_or(pdf.len()),
|
||||
b'/' if word(i - 1, b"/Filter") => filtered = true,
|
||||
b'e' if word(i - 1, b"endobj") => (depth, filtered) = (0, false),
|
||||
b'e' if word(i - 1, b"endstream") => i += 8,
|
||||
b's' if word(i - 1, b"stream") => {
|
||||
depth = 0;
|
||||
if std::mem::take(&mut filtered) {
|
||||
i = after(i, b"endstream").unwrap_or(pdf.len());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if depth > MAX_OBJECT_NESTING {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Read a PDF, decrypting it with `password` (none: the empty user password). The permissions
|
||||
/// password opens a file too.
|
||||
pub(crate) fn open(bytes: &[u8], password: Option<&str>) -> Result<Pdf, PdfError> {
|
||||
if is_postscript(bytes) {
|
||||
return Err(PdfError::PostScript);
|
||||
}
|
||||
if nests_too_deep(bytes) {
|
||||
return Err(too_deep());
|
||||
}
|
||||
let password = password.unwrap_or_default();
|
||||
let read = |pw: &str| Pdf::new_with_password(bytes.to_vec(), pw);
|
||||
read(password)
|
||||
@@ -57,6 +121,12 @@ pub(crate) fn open(bytes: &[u8], password: Option<&str>) -> Result<Pdf, PdfError
|
||||
})
|
||||
}
|
||||
|
||||
/// The error of a page whose content, decoded, nests arrays and dictionaries too deep (see
|
||||
/// [`MAX_OBJECT_NESTING`]): its operands are read the same way as the file's objects.
|
||||
pub(crate) fn content_too_deep(page: &Page<'_>) -> Result<(), PdfError> {
|
||||
if page.page_stream().is_some_and(nests_too_deep) { Err(too_deep()) } else { Ok(()) }
|
||||
}
|
||||
|
||||
/// The pages and their boxes. An encrypted PDF needs its `password`
|
||||
/// ([`PdfError::NeedsPassword`] / [`PdfError::WrongPassword`]).
|
||||
pub fn info(bytes: &[u8], password: Option<&str>) -> Result<PdfInfo, PdfError> {
|
||||
|
||||
+34
-14
@@ -18,10 +18,13 @@ use crate::{ImportOptions, SvgError, fnv1a};
|
||||
use css::XNode;
|
||||
use text::TextSlots;
|
||||
|
||||
pub use depth::MAX_ELEMENT_DEPTH;
|
||||
pub(crate) use depth::xml_too_deep;
|
||||
pub(crate) use fx::BLEND;
|
||||
|
||||
mod css;
|
||||
mod css_transform;
|
||||
mod depth;
|
||||
mod files;
|
||||
mod fx;
|
||||
mod spread;
|
||||
@@ -29,11 +32,30 @@ mod symbol_units;
|
||||
mod text;
|
||||
mod vector_effect;
|
||||
|
||||
/// The XML of `svg`.
|
||||
fn parse(svg: &str) -> Result<roxmltree::Document<'_>, SvgError> {
|
||||
roxmltree::Document::parse_with_options(svg, roxmltree::ParsingOptions { allow_dtd: true, ..Default::default() })
|
||||
.map_err(|e| SvgError::Parse(e.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn import(svg: &str, opts: &ImportOptions) -> Result<(Document, Vec<String>), SvgError> {
|
||||
let (svg, found) = prepass(svg, opts);
|
||||
// Before anything reads the tree: the XML reader, usvg, and what is made of its tree, all by
|
||||
// recursion.
|
||||
let too_deep = || SvgError::TooDeep(depth::MAX_ELEMENT_DEPTH);
|
||||
if depth::xml_too_deep(svg) {
|
||||
return Err(too_deep());
|
||||
}
|
||||
let xml = parse(svg)?;
|
||||
if depth::too_deep(&xml) {
|
||||
return Err(too_deep());
|
||||
}
|
||||
let (svg, found) = prepass(svg, &xml, opts);
|
||||
// The prepass's edits are in the text: the XML is read again only when it made any.
|
||||
let xml = match &svg {
|
||||
Cow::Borrowed(_) => xml,
|
||||
Cow::Owned(edited) => parse(edited)?,
|
||||
};
|
||||
let svg = svg.as_ref();
|
||||
let xml = roxmltree::Document::parse_with_options(svg, roxmltree::ParsingOptions { allow_dtd: true, ..Default::default() })
|
||||
.map_err(|e| SvgError::Parse(e.to_string()))?;
|
||||
let units = RootUnits::of(xml.root_element());
|
||||
let root_name = root_layer_name(xml.root_element());
|
||||
let mut warnings = found.warnings;
|
||||
@@ -165,6 +187,7 @@ pub(crate) fn import(svg: &str, opts: &ImportOptions) -> Result<(Document, Vec<S
|
||||
promote_sublayers(Arc::make_mut(l), &im.sublayer_groups);
|
||||
}
|
||||
}
|
||||
im.doc.limit_depth_noting(&mut im.warnings);
|
||||
Ok((im.doc, im.warnings))
|
||||
}
|
||||
|
||||
@@ -554,7 +577,7 @@ impl Edits {
|
||||
/// * linked files are read ([`files`]);
|
||||
/// * percentage lengths in a `<symbol>` with a `viewBox` become numbers ([`symbol_units`]);
|
||||
/// * what style sheet rules with a `:not()` set goes in `style` attributes ([`not_rules`]).
|
||||
fn prepass<'s>(svg: &'s str, opts: &ImportOptions) -> (Cow<'s, str>, Found) {
|
||||
fn prepass<'s>(svg: &'s str, xml: &roxmltree::Document, opts: &ImportOptions) -> (Cow<'s, str>, Found) {
|
||||
let mut found = Found::default();
|
||||
// (`:` for prefixed elements such as `<svg:image>`.)
|
||||
let css_transform = has_css_transform(svg);
|
||||
@@ -565,9 +588,6 @@ fn prepass<'s>(svg: &'s str, opts: &ImportOptions) -> (Cow<'s, str>, Found) {
|
||||
{
|
||||
return (svg.into(), found);
|
||||
}
|
||||
let Ok(xml) = roxmltree::Document::parse_with_options(svg, roxmltree::ParsingOptions { allow_dtd: true, ..Default::default() }) else {
|
||||
return (svg.into(), found);
|
||||
};
|
||||
let mut edits = Edits::default();
|
||||
let elements = || xml.descendants().filter(|n| n.is_element());
|
||||
for a in elements().filter(|n| n.tag_name().name() == "a") {
|
||||
@@ -592,24 +612,24 @@ fn prepass<'s>(svg: &'s str, opts: &ImportOptions) -> (Cow<'s, str>, Found) {
|
||||
}
|
||||
let (display, non_scaling) = (svg.contains("display"), svg.contains(vector_effect::NON_SCALING));
|
||||
if display || non_scaling || not {
|
||||
let css = css::Styles::new(&xml);
|
||||
let css = css::Styles::new(xml);
|
||||
// First: an undisplayed object shown for usvg is shown after these.
|
||||
if not && css.has_not_rules() {
|
||||
not_rules(svg, &xml, &css, &mut edits);
|
||||
not_rules(svg, xml, &css, &mut edits);
|
||||
}
|
||||
if display {
|
||||
hidden_objects(svg, &xml, &css, &mut edits, &mut found.hidden);
|
||||
hidden_objects(svg, xml, &css, &mut edits, &mut found.hidden);
|
||||
}
|
||||
if non_scaling {
|
||||
vector_effect::find(svg, &xml, &css, &mut edits, &mut found.non_scaling, &mut found.warnings);
|
||||
vector_effect::find(svg, xml, &css, &mut edits, &mut found.non_scaling, &mut found.warnings);
|
||||
}
|
||||
}
|
||||
found.files = files::Files::read(svg, &xml, opts, &mut edits, &mut found.warnings);
|
||||
found.files = files::Files::read(svg, xml, opts, &mut edits, &mut found.warnings);
|
||||
if !symbols.is_empty() && svg.contains('%') {
|
||||
edits.list.extend(symbol_units::rewrites(&xml));
|
||||
edits.list.extend(symbol_units::rewrites(xml));
|
||||
}
|
||||
if css_transform {
|
||||
edits.list.extend(css_transform::rewrites(svg, &xml));
|
||||
edits.list.extend(css_transform::rewrites(svg, xml));
|
||||
}
|
||||
(edits.apply(svg), found)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ pub(super) type XNode<'a, 'i> = roxmltree::Node<'a, 'i>;
|
||||
|
||||
/// `name: value` declarations of a block or `style` attribute, with their `!important` flag. The
|
||||
/// `font` shorthand becomes its longhands, as usvg reads it for the rest of the drawing (#1557).
|
||||
fn parse_decls(s: &str) -> Vec<(String, String, bool)> {
|
||||
pub(super) fn parse_decls(s: &str) -> Vec<(String, String, bool)> {
|
||||
s.split(';')
|
||||
.filter_map(|d| {
|
||||
let (k, v) = d.split_once(':')?;
|
||||
@@ -371,8 +371,13 @@ impl Styles {
|
||||
Self { rules, index, cache: RefCell::default() }
|
||||
}
|
||||
|
||||
/// Do the document's style sheets have any rules?
|
||||
pub(super) fn has_rules(&self) -> bool {
|
||||
!self.rules.is_empty()
|
||||
}
|
||||
|
||||
/// Properties set on `n` by rules and its `style` attribute, after the cascade.
|
||||
fn declared(&self, n: XNode) -> Rc<HashMap<String, String>> {
|
||||
pub(super) fn declared(&self, n: XNode) -> Rc<HashMap<String, String>> {
|
||||
if let Some(m) = self.cache.borrow().get(&n.id()) {
|
||||
return m.clone();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
//! How deep an SVG's elements nest, found in the XML before usvg runs: usvg converts the tree by
|
||||
//! recursion, a few stack frames an element, so a file nested deeply enough overflows the stack
|
||||
//! (an abort, not an error). Such a file is refused: its nesting can't be merged before usvg has
|
||||
//! read it, and usvg is what can't read it.
|
||||
//!
|
||||
//! What counts is the tree usvg walks, not the XML alone: a `<use>` holds a copy of what it
|
||||
//! refers to, and a clip path, mask, pattern, marker or filter is converted where it is used
|
||||
//! (`clip-path: url(#a)` on an element of a clip path makes a chain as deep as there are clip
|
||||
//! paths). So each element's depth is the deepest of what it holds and what it refers to.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use usvg::roxmltree;
|
||||
|
||||
use super::css::{Styles, XNode, parse_decls};
|
||||
|
||||
/// The deepest an SVG's elements may nest, references followed. Far beyond what drawing apps
|
||||
/// write, and deeper than a document's objects may nest ([`vectorcraft_doc::MAX_DEPTH`]: wrappers
|
||||
/// that hold one object aren't kept, and what is still deeper is merged into the group around
|
||||
/// it). usvg uses about 15 kB of stack for each level: the importer runs on a thread with 16 MB
|
||||
/// ([`vectorcraft_doc::depth::with_deep_stack`]), except on the web, where there is the one
|
||||
/// 1 MB stack.
|
||||
pub const MAX_ELEMENT_DEPTH: usize = if cfg!(target_arch = "wasm32") { 48 } else { 256 };
|
||||
|
||||
/// Properties whose value an element's descendants use too: the paint server they name is
|
||||
/// converted at each descendant, under everything between.
|
||||
const INHERITED: [&str; 6] = ["fill", "stroke", "marker", "marker-start", "marker-mid", "marker-end"];
|
||||
|
||||
/// The ids of the `url(#id)` references in a property value.
|
||||
fn urls(value: &str) -> impl Iterator<Item = &str> {
|
||||
value.split("url(").skip(1).filter_map(|rest| {
|
||||
let id = rest.trim_start().trim_start_matches(['"', '\'']).strip_prefix('#')?;
|
||||
id.split([')', '"', '\'']).next().map(str::trim).filter(|id| !id.is_empty())
|
||||
})
|
||||
}
|
||||
|
||||
/// The elements `n` refers to, each with whether `n`'s descendants use it too.
|
||||
fn references<'a, 'i>(n: XNode<'a, 'i>, ids: &HashMap<&str, XNode<'a, 'i>>, css: &Styles, out: &mut Vec<(XNode<'a, 'i>, bool)>) {
|
||||
let add = |out: &mut Vec<(XNode<'a, 'i>, bool)>, name: &str, value: &str| {
|
||||
let inherited = INHERITED.contains(&name);
|
||||
out.extend(urls(value).filter_map(|id| ids.get(id)).map(|t| (*t, inherited)));
|
||||
};
|
||||
for a in n.attributes() {
|
||||
match a.name() {
|
||||
"href" => out.extend(a.value().trim().strip_prefix('#').and_then(|id| ids.get(id)).map(|t| (*t, false))),
|
||||
// Read with the style sheets' rules below.
|
||||
"style" if css.has_rules() => {}
|
||||
"style" => parse_decls(a.value()).iter().for_each(|(k, v, _)| add(out, k, v)),
|
||||
name => add(out, name, a.value()),
|
||||
}
|
||||
}
|
||||
if css.has_rules() {
|
||||
css.declared(n).iter().for_each(|(k, v)| add(out, k, v));
|
||||
}
|
||||
}
|
||||
|
||||
/// Do the elements of the XML text `svg` nest deeper than [`MAX_ELEMENT_DEPTH`]? Read from the
|
||||
/// text, before any XML reader: roxmltree (ours and usvg's) reads elements by recursion too. Tags
|
||||
/// are counted as the text has them; markup in the values of a document type's entities counts
|
||||
/// ten times (how deep entities may refer to one another), on top of the deepest element.
|
||||
pub(crate) fn xml_too_deep(svg: &str) -> bool {
|
||||
let b = svg.as_bytes();
|
||||
let find =
|
||||
|from: usize, what: &[u8]| b.get(from..).and_then(|rest| rest.windows(what.len()).position(|w| w == what)).map(|at| from + at + what.len());
|
||||
// The end of the tag starting at `from` (past its `>`), and whether it closes itself.
|
||||
let tag_end = |from: usize| {
|
||||
let mut quote = None;
|
||||
for (i, c) in b.iter().enumerate().skip(from) {
|
||||
match (quote, c) {
|
||||
(None, b'"' | b'\'') => quote = Some(*c),
|
||||
(Some(q), c) if q == *c => quote = None,
|
||||
(None, b'>') => return (i + 1, b.get(i.wrapping_sub(1)) == Some(&b'/')),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
(b.len(), false)
|
||||
};
|
||||
// (now, deepest) of the elements, and of the markup inside the document type.
|
||||
let (mut elements, mut entities) = ((0usize, 0usize), (0usize, 0usize));
|
||||
let mut doctype_end = 0;
|
||||
let mut i = 0;
|
||||
while let Some(at) = b.get(i..).and_then(|rest| rest.iter().position(|c| *c == b'<')).map(|at| i + at) {
|
||||
let rest = b.get(at..).unwrap_or_default();
|
||||
let depth = if at < doctype_end { &mut entities } else { &mut elements };
|
||||
i = if rest.starts_with(b"<!--") {
|
||||
find(at, b"-->").unwrap_or(b.len())
|
||||
} else if rest.starts_with(b"<![CDATA[") {
|
||||
find(at, b"]]>").unwrap_or(b.len())
|
||||
} else if rest.starts_with(b"<?") {
|
||||
find(at, b"?>").unwrap_or(b.len())
|
||||
} else if rest.starts_with(b"<!DOCTYPE") {
|
||||
// Up to the `>` that ends it: after its internal subset (`[` … `]`) when it has one.
|
||||
let plain = tag_end(at).0;
|
||||
let subset = b.get(at..plain).is_some_and(|head| head.contains(&b'['));
|
||||
doctype_end = if subset { find(at, b"]>").unwrap_or(b.len()) } else { plain };
|
||||
at + 2
|
||||
} else if rest.starts_with(b"<!") {
|
||||
// A declaration of the document type: the markup in its value is counted as it comes.
|
||||
at + 2
|
||||
} else if rest.starts_with(b"</") {
|
||||
depth.0 = depth.0.saturating_sub(1);
|
||||
tag_end(at).0
|
||||
} else if at < doctype_end {
|
||||
// In an entity's value, itself in quotes: up to the tag's `>`.
|
||||
let end = find(at, b">").unwrap_or(b.len());
|
||||
depth.1 = depth.1.max(depth.0 + 1);
|
||||
depth.0 += usize::from(b.get(end.wrapping_sub(2)) != Some(&b'/'));
|
||||
end
|
||||
} else {
|
||||
let (end, empty) = tag_end(at);
|
||||
depth.1 = depth.1.max(depth.0 + 1);
|
||||
depth.0 += usize::from(!empty);
|
||||
end
|
||||
};
|
||||
if elements.1 + 10 * entities.1 > MAX_ELEMENT_DEPTH {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Does anything in `xml` nest deeper than [`MAX_ELEMENT_DEPTH`], references followed? Without
|
||||
/// recursion.
|
||||
pub(super) fn too_deep(xml: &roxmltree::Document) -> bool {
|
||||
let elements = || xml.descendants().filter(|n| n.is_element());
|
||||
let ids: HashMap<&str, XNode> = elements().filter_map(|n| Some((n.attribute("id")?, n))).collect();
|
||||
let css = Styles::new(xml);
|
||||
// How deep each element's own tree goes, itself included; 0 while it is being worked out: a
|
||||
// reference back to it then is a loop, which usvg leaves out.
|
||||
let mut heights: HashMap<roxmltree::NodeId, usize> = HashMap::new();
|
||||
enum Step<'a, 'i> {
|
||||
Enter(XNode<'a, 'i>),
|
||||
Leave(XNode<'a, 'i>, Vec<(XNode<'a, 'i>, bool)>),
|
||||
}
|
||||
let mut stack = vec![Step::Enter(xml.root_element())];
|
||||
while let Some(step) = stack.pop() {
|
||||
match step {
|
||||
Step::Enter(n) => {
|
||||
match heights.entry(n.id()) {
|
||||
Entry::Occupied(_) => continue,
|
||||
Entry::Vacant(v) => v.insert(0),
|
||||
};
|
||||
let mut refs = vec![];
|
||||
references(n, &ids, &css, &mut refs);
|
||||
let targets: Vec<XNode> = refs.iter().map(|(t, _)| *t).collect();
|
||||
stack.push(Step::Leave(n, refs));
|
||||
stack.extend(n.children().filter(|c| c.is_element()).chain(targets).map(Step::Enter));
|
||||
}
|
||||
Step::Leave(n, refs) => {
|
||||
let of = |n: XNode| heights.get(&n.id()).copied().unwrap_or(0);
|
||||
let held = n.children().filter(|c| c.is_element()).map(of).max().unwrap_or(0);
|
||||
let used = |inherited: bool| refs.iter().filter(|(_, i)| *i == inherited).map(|(t, _)| of(*t)).max().unwrap_or(0);
|
||||
let height = 1 + held.max(used(false)) + used(true);
|
||||
if height > MAX_ELEMENT_DEPTH {
|
||||
return true;
|
||||
}
|
||||
heights.insert(n.id(), height);
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn deep(svg: &str) -> bool {
|
||||
too_deep(&roxmltree::Document::parse(svg).unwrap())
|
||||
}
|
||||
|
||||
fn svg(body: &str) -> String {
|
||||
format!(r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="10" height="10">{body}</svg>"#)
|
||||
}
|
||||
|
||||
/// `n` groups in one another around a rectangle.
|
||||
fn groups(n: usize) -> String {
|
||||
format!("{}<rect width='1' height='1'/>{}", "<g>".repeat(n), "</g>".repeat(n))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn elements_nest_up_to_the_cap() {
|
||||
// The root, the groups and the rectangle.
|
||||
let (at, past) = (svg(&groups(MAX_ELEMENT_DEPTH - 2)), svg(&groups(MAX_ELEMENT_DEPTH - 1)));
|
||||
assert!(!deep(&at) && !xml_too_deep(&at));
|
||||
assert!(deep(&past) && xml_too_deep(&past));
|
||||
assert!(xml_too_deep(&svg(&groups(200_000))), "any depth is measured");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_text_is_measured_as_xml_reads_it() {
|
||||
let many = r#"<g a='>' b="<g>"><rect/></g>"#.repeat(500);
|
||||
assert!(!xml_too_deep(&svg(&many)), "side by side, with `>` and tags in attribute values");
|
||||
let hidden = format!("<!-- {0} --><![CDATA[{0}]]><?pi {0}?>", "<g>".repeat(500));
|
||||
assert!(!xml_too_deep(&svg(&hidden)), "comments, character data and instructions hold no elements");
|
||||
assert!(xml_too_deep(&format!("<svg>{}", "<g>".repeat(MAX_ELEMENT_DEPTH))), "never closed");
|
||||
assert!(!xml_too_deep(&format!("{}<svg/>", "</g>".repeat(2 * MAX_ELEMENT_DEPTH))), "ends without beginnings");
|
||||
// Entities: namespaces as drawing apps declare them, and markup that would be read ten deep.
|
||||
let names = r#"<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [ <!ENTITY ns_svg "http://www.w3.org/2000/svg"> ]>"#;
|
||||
assert!(!xml_too_deep(&format!("{names}{}", svg(&groups(30)))));
|
||||
let tenth = MAX_ELEMENT_DEPTH / 10 + 1;
|
||||
let bomb = format!("<!DOCTYPE svg [ <!ENTITY a \"{}&a;{}\"> ]>", "<g>".repeat(tenth), "</g>".repeat(tenth));
|
||||
assert!(xml_too_deep(&format!("{bomb}<svg>&a;</svg>")));
|
||||
assert!(!xml_too_deep(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_chain_of_uses_is_as_deep_as_what_it_copies() {
|
||||
let chain = |n: usize| {
|
||||
let defs: String = (0..n).map(|i| format!("<g id='g{i}'><use xlink:href='#g{}'/></g>", i + 1)).collect();
|
||||
svg(&format!("<defs>{defs}<rect id='g{n}' width='1' height='1'/></defs><use href='#g0'/>"))
|
||||
};
|
||||
assert!(!deep(&chain(10)));
|
||||
assert!(deep(&chain(MAX_ELEMENT_DEPTH / 2)), "two elements a link");
|
||||
assert!(deep(&chain(5000)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chains_of_clips_masks_and_patterns_count_by_attribute_style_and_style_sheet() {
|
||||
let by_attribute = |n: usize| -> String {
|
||||
(0..n).map(|i| format!("<clipPath id='c{i}' clip-path='url(#c{})'><rect width='1' height='1'/></clipPath>", i + 1)).collect()
|
||||
};
|
||||
let by_style = |n: usize| -> String {
|
||||
(0..n).map(|i| format!("<mask id='c{i}' style='mask: url( \"#c{}\" )'><rect width='1' height='1'/></mask>", i + 1)).collect()
|
||||
};
|
||||
let by_sheet = |n: usize| -> String {
|
||||
let rules: String = (0..n).map(|i| format!(".p{i} {{ fill: url(#c{}) }}", i + 1)).collect();
|
||||
let patterns: String = (0..n)
|
||||
.map(|i| format!("<pattern id='c{i}' width='1' height='1'><g class='p{i}'><rect width='1' height='1'/></g></pattern>"))
|
||||
.collect();
|
||||
format!("<style>{rules}</style>{patterns}")
|
||||
};
|
||||
for chain in [&by_attribute as &dyn Fn(usize) -> String, &by_style, &by_sheet] {
|
||||
let file =
|
||||
|n| svg(&format!("<defs>{}</defs><rect width='5' height='5' fill='url(#c0)' clip-path='url(#c0)' mask='url(#c0)'/>", chain(n)));
|
||||
assert!(!deep(&file(5)));
|
||||
assert!(deep(&file(MAX_ELEMENT_DEPTH)));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn many_paint_servers_used_side_by_side_are_not_a_chain() {
|
||||
// As drawing apps write gradients: a class each, used by objects that aren't inside one another.
|
||||
let rules: String = (0..300).map(|i| format!(".c{i}{{fill:url(#g{i})}}")).collect();
|
||||
let defs: String = (0..300).map(|i| format!("<linearGradient id='g{i}'><stop class='c{i}' offset='0'/></linearGradient>")).collect();
|
||||
let art: String = (0..300).map(|i| format!("<g class='c{i}'><rect class='c{i}' width='1' height='1'/></g>")).collect();
|
||||
assert!(!deep(&svg(&format!("<style>{rules}</style><defs>{defs}</defs>{art}"))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_loop_of_references_is_not_deep() {
|
||||
assert!(!deep(&svg("<g id='a'><use href='#b'/></g><g id='b'><use href='#a'/></g><clipPath id='c' clip-path='url(#c)'/>")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_inherited_paint_is_converted_under_everything_between() {
|
||||
// Neither the pattern nor the art is too deep; the pattern under the art's last object is.
|
||||
let half = MAX_ELEMENT_DEPTH / 2;
|
||||
let pattern = format!("<pattern id='p' width='1' height='1'>{}</pattern>", groups(half));
|
||||
assert!(!deep(&svg(&format!("<defs>{pattern}</defs><g fill='url(#p)'>{}</g>", groups(half / 2)))));
|
||||
assert!(deep(&svg(&format!("<defs>{pattern}</defs><g fill='url(#p)'>{}</g>", groups(half)))));
|
||||
}
|
||||
}
|
||||
@@ -203,12 +203,16 @@ impl Files {
|
||||
ImageHrefResolver {
|
||||
resolve_data: Box::new(move |mime, bytes, opts| {
|
||||
if mime == "image/svg+xml" {
|
||||
if nested_too_deep(&bytes) {
|
||||
return None;
|
||||
}
|
||||
self.note_text(&bytes);
|
||||
}
|
||||
data(mime, bytes, opts)
|
||||
}),
|
||||
resolve_string: Box::new(move |h, opts| match self.sources.get(h).or_else(|| self.sources.get(h.trim()))? {
|
||||
Source::Raster(kind, bytes) => Some(kind(bytes.clone())),
|
||||
Source::Svg(bytes) if nested_too_deep(bytes) => None,
|
||||
Source::Svg(bytes) => {
|
||||
self.note_text(bytes);
|
||||
usvg::Tree::from_data_nested(bytes, opts).ok().map(ImageKind::SVG)
|
||||
@@ -218,6 +222,12 @@ impl Files {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the SVG image `bytes` nested too deep for usvg to read (as the SVG holding it would be)?
|
||||
/// It is then left out, as an image usvg can't read.
|
||||
fn nested_too_deep(bytes: &[u8]) -> bool {
|
||||
crate::text_of(bytes).is_ok_and(|text| super::xml_too_deep(&text))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
+11
-3
@@ -312,6 +312,9 @@ pub struct LinkedImage {
|
||||
pub enum SvgError {
|
||||
#[error("SVG parse error: {0}")]
|
||||
Parse(String),
|
||||
/// Its elements nest (references followed) deeper than the reader goes.
|
||||
#[error("the SVG's elements are nested more than {0} deep")]
|
||||
TooDeep(usize),
|
||||
}
|
||||
|
||||
/// Export a document as an SVG string (linked images are not returned: see [`export_full`]).
|
||||
@@ -347,7 +350,8 @@ pub struct Editing {
|
||||
|
||||
/// The native document an SVG carries, if any, and whether the SVG was edited since.
|
||||
pub fn editing(svg: &str) -> Option<Editing> {
|
||||
if !svg.contains(EDITING_NS) {
|
||||
// (An SVG nested too deep for the XML reader carries none: it isn't one of ours.)
|
||||
if !svg.contains(EDITING_NS) || import::xml_too_deep(svg) {
|
||||
return None;
|
||||
}
|
||||
let opts = usvg::roxmltree::ParsingOptions { allow_dtd: true, ..Default::default() };
|
||||
@@ -500,14 +504,18 @@ pub fn import_with_report(svg: &str) -> Result<(Document, Vec<String>), SvgError
|
||||
import_with(svg, &ImportOptions::default())
|
||||
}
|
||||
|
||||
pub use import::MAX_ELEMENT_DEPTH;
|
||||
|
||||
/// Import an SVG document reading the files it links to as `opts` says, also returning warnings.
|
||||
pub fn import_with(svg: &str, opts: &ImportOptions) -> Result<(Document, Vec<String>), SvgError> {
|
||||
import::import(svg, opts)
|
||||
// usvg converts the tree by recursion, in large stack frames: the nesting it is given is
|
||||
// capped (`import::depth`), and it gets room for that cap whatever thread asks.
|
||||
vectorcraft_doc::depth::with_deep_stack(|| import::import(svg, opts))
|
||||
}
|
||||
|
||||
/// Reads a linked file: its bytes and the link to it (absolute path, size, time and hash), or
|
||||
/// `None` when there is no readable file at the (absolute) path.
|
||||
pub type ReadFile<'a> = &'a dyn Fn(&str) -> Option<(Vec<u8>, vectorcraft_doc::LinkInfo)>;
|
||||
pub type ReadFile<'a> = &'a (dyn Fn(&str) -> Option<(Vec<u8>, vectorcraft_doc::LinkInfo)> + Sync);
|
||||
|
||||
/// How [`import_with`] reads the files an SVG's `<image>` elements link to.
|
||||
///
|
||||
|
||||
@@ -25,6 +25,8 @@ const MAX_STOPS: usize = 6;
|
||||
const MIN_SPACING: f64 = 0.08;
|
||||
/// How far (levels) the stops may be from the colours along the gradient.
|
||||
const STOP_TOLERANCE: f64 = 2.0;
|
||||
/// How far (levels) a pixel beside a gradient may be from the gradient's colour there and join it.
|
||||
const GROW: f64 = 10.0;
|
||||
|
||||
/// A linear gradient a traced path is filled with.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@@ -52,6 +54,12 @@ impl TracedGradient {
|
||||
}
|
||||
}
|
||||
|
||||
/// The pixels beside pixel `i` of a `w` × `h` image.
|
||||
fn beside(i: usize, w: usize, h: usize) -> impl Iterator<Item = usize> {
|
||||
let (x, y) = (i % w, i / w);
|
||||
[(x > 0).then(|| i - 1), (x + 1 < w).then(|| i + 1), (y > 0).then(|| i - w), (y + 1 < h).then(|| i + w)].into_iter().flatten()
|
||||
}
|
||||
|
||||
/// The gradients of `img`: for each pixel the gradient it is part of (an index into the list, or
|
||||
/// `u32::MAX`), and the list.
|
||||
pub(crate) fn detect(img: &Raster) -> (Vec<u32>, Vec<TracedGradient>) {
|
||||
@@ -103,11 +111,8 @@ pub(crate) fn detect(img: &Raster) -> (Vec<u32>, Vec<TracedGradient>) {
|
||||
let mut depth = vec![0u16; w * h];
|
||||
let mut found = vec![];
|
||||
let (mut members, mut stack) = (vec![], vec![]);
|
||||
for seed in 0..w * h {
|
||||
if seen.get(seed).copied().unwrap_or(true) || !smooth.get(seed).copied().unwrap_or(false) {
|
||||
continue;
|
||||
}
|
||||
// The connected smooth pixels from here.
|
||||
// The connected smooth pixels from `seed` that `seen` has not: into `members`.
|
||||
let mut flood = |seed: usize, seen: &mut [bool], members: &mut Vec<usize>| {
|
||||
members.clear();
|
||||
stack.push(seed);
|
||||
if let Some(s) = seen.get_mut(seed) {
|
||||
@@ -115,8 +120,7 @@ pub(crate) fn detect(img: &Raster) -> (Vec<u32>, Vec<TracedGradient>) {
|
||||
}
|
||||
while let Some(i) = stack.pop() {
|
||||
members.push(i);
|
||||
let (x, y) = (i % w, i / w);
|
||||
for j in [(x > 0).then(|| i - 1), (x + 1 < w).then(|| i + 1), (y > 0).then(|| i - w), (y + 1 < h).then(|| i + w)].into_iter().flatten() {
|
||||
for j in beside(i, w, h) {
|
||||
if smooth.get(j).copied().unwrap_or(false) && !seen.get(j).copied().unwrap_or(true) {
|
||||
if let Some(s) = seen.get_mut(j) {
|
||||
*s = true;
|
||||
@@ -125,7 +129,26 @@ pub(crate) fn detect(img: &Raster) -> (Vec<u32>, Vec<TracedGradient>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if members.len() < MIN_PIXELS || found.len() >= 1000 {
|
||||
};
|
||||
// The areas, largest first: a gradient that noise or a soft rim broke into several is found in
|
||||
// its largest piece and grown over the rest.
|
||||
let mut areas: Vec<(usize, usize)> = vec![];
|
||||
for seed in 0..w * h {
|
||||
if !seen.get(seed).copied().unwrap_or(true) && smooth.get(seed).copied().unwrap_or(false) {
|
||||
flood(seed, &mut seen, &mut members);
|
||||
if members.len() >= MIN_PIXELS {
|
||||
areas.push((members.len(), seed));
|
||||
}
|
||||
}
|
||||
}
|
||||
areas.sort_unstable_by(|a, b| b.cmp(a));
|
||||
seen.fill(false);
|
||||
for (_, seed) in areas {
|
||||
if seen.get(seed).copied().unwrap_or(true) || found.len() >= 1000 {
|
||||
continue;
|
||||
}
|
||||
flood(seed, &mut seen, &mut members);
|
||||
if members.len() < MIN_PIXELS {
|
||||
continue;
|
||||
}
|
||||
// The gradient is read from the area's inside, where there is one: the pixels at its rim
|
||||
@@ -135,16 +158,12 @@ pub(crate) fn detect(img: &Raster) -> (Vec<u32>, Vec<TracedGradient>) {
|
||||
let rim = far as usize + 2;
|
||||
let unknown = u16::MAX;
|
||||
members.iter().for_each(|&i| depth[i] = unknown);
|
||||
let beside = |i: usize| {
|
||||
let (x, y) = (i % w, i / w);
|
||||
[(x > 0).then(|| i - 1), (x + 1 < w).then(|| i + 1), (y > 0).then(|| i - w), (y + 1 < h).then(|| i + w)].into_iter().flatten()
|
||||
};
|
||||
let mut front: Vec<usize> = members.iter().copied().filter(|&i| beside(i).any(|j| depth.get(j).copied() == Some(0))).collect();
|
||||
let mut front: Vec<usize> = members.iter().copied().filter(|&i| beside(i, w, h).any(|j| depth.get(j).copied() == Some(0))).collect();
|
||||
front.iter().for_each(|&i| depth[i] = 1);
|
||||
for d in 2..=rim as u16 {
|
||||
let mut next = vec![];
|
||||
for &i in &front {
|
||||
for j in beside(i) {
|
||||
for j in beside(i, w, h) {
|
||||
if let Some(slot) = depth.get_mut(j).filter(|s| **s == unknown) {
|
||||
*slot = d;
|
||||
next.push(j);
|
||||
@@ -156,12 +175,27 @@ pub(crate) fn detect(img: &Raster) -> (Vec<u32>, Vec<TracedGradient>) {
|
||||
let inside: Vec<usize> = members.iter().copied().filter(|&i| depth.get(i).copied() == Some(unknown)).collect();
|
||||
let fitted = fit(img, w, if inside.len() >= MIN_PIXELS { &inside } else { &members }, &members, rim);
|
||||
members.iter().for_each(|&i| depth[i] = 0);
|
||||
if let Some((g, part)) = fitted {
|
||||
if let Some((g, mut part)) = fitted {
|
||||
for &i in &part {
|
||||
if let Some(o) = of.get_mut(i) {
|
||||
*o = found.len() as u32;
|
||||
}
|
||||
}
|
||||
// Grown over every pixel next to it that has its colour there: the pixels noise put a
|
||||
// little off, a soft rim, the pieces noise cut off it.
|
||||
while let Some(i) = part.pop() {
|
||||
for j in beside(i, w, h) {
|
||||
let near = px(j).is_some_and(|c| {
|
||||
let own = g.color_at(Point::new((j % w) as f64 + 0.5, (j / w) as f64 + 0.5));
|
||||
(0..3).all(|k| (f64::from(c[k]) - own[k]).abs() <= GROW)
|
||||
});
|
||||
if near && of.get(j).copied() == Some(u32::MAX) {
|
||||
of[j] = found.len() as u32;
|
||||
seen[j] = true;
|
||||
part.push(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
found.push(g);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@ fn inside_or_near(p: Point, o: &Outer) -> bool {
|
||||
/// path whose outer contour holds all of its own. The path that covers the whole `w` × `h` image
|
||||
/// (the paper) is nobody's parent: everything would be inside it.
|
||||
///
|
||||
/// Drawing each path followed by those inside it paints the picture `paths` paints in its own
|
||||
/// order: where the paths can overlap (`stacked`), a path only gets a parent below it, and only
|
||||
/// Drawing each path together with those inside it (in their own order) paints the picture
|
||||
/// `paths` paints in its own order: where the paths can overlap (`stacked`), a path only gets a parent below it, and only
|
||||
/// if no path that would then come after it instead of before it shares any of its box.
|
||||
pub(crate) fn parents(paths: &[TracedPath], w: usize, h: usize, stacked: bool) -> Vec<Option<usize>> {
|
||||
let outers: Vec<Option<Outer>> = paths.iter().map(outer).collect();
|
||||
|
||||
+63
-2
@@ -430,6 +430,14 @@ pub fn presets() -> Vec<(&'static str, TraceParams)> {
|
||||
PRESET_NAMES.iter().filter_map(|n| Some((*n, preset(n)?))).collect()
|
||||
}
|
||||
|
||||
/// The enhanced version of built-in preset `name`: the preset with the options the enhanced
|
||||
/// presets have built in: Auto Grouping in all of them, Shapes in all but High Fidelity Photo.
|
||||
/// Gradients stay an option to turn on. Flat Logo, a preset of our own, has none.
|
||||
pub fn enhanced_preset(name: &str) -> Option<TraceParams> {
|
||||
let p = preset(name).filter(|p| p.mode != Mode::Logo)?;
|
||||
Some(TraceParams { auto_grouping: true, shapes: !name.eq_ignore_ascii_case("High Fidelity Photo"), ..p })
|
||||
}
|
||||
|
||||
/// One traced shape: an outer contour plus its holes, filled with `color`, or (with `stroke`)
|
||||
/// lines stroked with it.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@@ -458,7 +466,8 @@ pub struct TraceResult {
|
||||
/// Colours actually used.
|
||||
pub palette: Vec<[u8; 3]>,
|
||||
/// Auto Grouping: for each of `paths`, the path it lies inside, if any (empty without the
|
||||
/// option). Each path drawn before those inside it paints what `paths` paints in its order.
|
||||
/// option). Each path drawn together with those inside it, in the order they have here, paints
|
||||
/// what `paths` paints in its order.
|
||||
pub parents: Vec<Option<usize>>,
|
||||
}
|
||||
|
||||
@@ -813,6 +822,36 @@ impl Layers<'_> {
|
||||
shift
|
||||
}
|
||||
|
||||
/// Whether the pixels inside `l` (a loop along pixel edges) all have one label.
|
||||
fn one_colour_within(&self, l: &Loop) -> bool {
|
||||
let mut first = None;
|
||||
let (y0, y1) = l.pts.iter().fold((i32::MAX, i32::MIN), |m, p| (m.0.min(p.1), m.1.max(p.1)));
|
||||
for y in y0..y1 {
|
||||
// The loop's upright edges that pass this row of pixels, left to right: the pixels
|
||||
// between the first and the second are inside, and so on.
|
||||
let mut xs: Vec<i32> = l
|
||||
.pts
|
||||
.iter()
|
||||
.zip(l.pts.iter().cycle().skip(1))
|
||||
.filter(|(a, b)| a.0 == b.0 && a.1.min(b.1) <= y && y < a.1.max(b.1))
|
||||
.map(|(a, _)| a.0)
|
||||
.collect();
|
||||
xs.sort_unstable();
|
||||
for pair in xs.as_chunks::<2>().0 {
|
||||
for x in pair[0]..pair[1] {
|
||||
let label = usize::try_from(y)
|
||||
.ok()
|
||||
.zip(usize::try_from(x).ok())
|
||||
.and_then(|(y, x)| self.q.labels.get(y * self.w + x).copied().filter(|_| x < self.w));
|
||||
if label.is_none() || *first.get_or_insert(label) != label {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
first.is_some()
|
||||
}
|
||||
|
||||
/// The paths of layer `r` (palette entry `ci`), or `None` once the anchors made pass the
|
||||
/// maximum.
|
||||
fn layer(&self, r: usize, ci: usize) -> Option<Vec<TracedPath>> {
|
||||
@@ -882,7 +921,29 @@ impl Layers<'_> {
|
||||
if hole.area2.abs() / 2 < min_hole && params.noise > 0 {
|
||||
continue;
|
||||
}
|
||||
subs.extend(fit(hole));
|
||||
let Some(mut sp) = fit(hole) else { continue };
|
||||
// Shapes: a hole that is a shape is that shape, as what fills it is (the two are
|
||||
// one outline: they stay one). Only where one colour fills it: the parts of
|
||||
// several are not that shape, and would no longer meet it.
|
||||
if params.shapes
|
||||
&& area_stroke.is_none()
|
||||
&& self.one_colour_within(hole)
|
||||
&& let Some(exact) = shapes::recognise(&sp, hard.get()).and_then(|shape| shape.path().subpaths.into_iter().next())
|
||||
{
|
||||
// (Which way round each goes: by the area of the polygon through points along it.)
|
||||
let turn = |sp: &vectorcraft_geom::SubPath| {
|
||||
use vectorcraft_geom::ParamCurve;
|
||||
let pts: Vec<_> =
|
||||
(0..sp.segment_count()).flat_map(|i| (0..6).map(move |k| sp.segment(i).eval(f64::from(k) / 6.0).to_vec2())).collect();
|
||||
pts.iter().zip(pts.iter().cycle().skip(1)).map(|(a, b)| a.cross(*b)).sum::<f64>()
|
||||
};
|
||||
let mut exact = exact;
|
||||
if (turn(&exact) < 0.0) != (turn(&sp) < 0.0) {
|
||||
exact.reverse();
|
||||
}
|
||||
sp = exact;
|
||||
}
|
||||
subs.push(sp);
|
||||
}
|
||||
// Shapes: a filled outline without holes that is a shape becomes that shape.
|
||||
let shape = match subs.as_slice() {
|
||||
|
||||
@@ -128,6 +128,17 @@ pub(crate) fn recognise(sp: &SubPath, hard: bool) -> Option<TracedShape> {
|
||||
for angle in side.into_iter().chain([folded(axis), 0.0]) {
|
||||
let xy: Vec<(f64, f64)> = pts.iter().map(|&p| local(p, angle)).collect();
|
||||
let (hw, hh) = xy.iter().fold((0.0f64, 0.0f64), |m, p| (m.0.max(p.0.abs()), m.1.max(p.1.abs())));
|
||||
// (Its half sizes: where its sides lie on average, not where their farthest points do.)
|
||||
let side = |far: f64, upright: bool, other: f64| {
|
||||
let on: Vec<f64> = xy
|
||||
.iter()
|
||||
.map(|p| if upright { (p.0, p.1) } else { (p.1, p.0) })
|
||||
.filter(|&(a, b)| a.abs() >= far - 0.5 && b.abs() <= other - CORNER)
|
||||
.map(|(a, _)| a.abs())
|
||||
.collect();
|
||||
if on.len() >= 4 { on.iter().sum::<f64>() / on.len() as f64 } else { far }
|
||||
};
|
||||
let (hw, hh) = (side(hw, true, hh), side(hh, false, hw));
|
||||
if hw <= 2.0 || hh <= 2.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ static ALLOC: Counting = Counting;
|
||||
|
||||
/// Whether `input` is what `preset` is meant for.
|
||||
fn target(preset: &str, input: &Input) -> bool {
|
||||
let preset = preset.strip_suffix(", enhanced").unwrap_or(preset);
|
||||
let classes: &[Class] = match preset {
|
||||
"High Fidelity Photo"
|
||||
| "Low Fidelity Photo"
|
||||
@@ -266,6 +267,11 @@ fn report() -> String {
|
||||
] {
|
||||
presets.push((name, TraceParams { gradients, ..vectorcraft_trace::preset(base).unwrap() }));
|
||||
}
|
||||
// Each preset's enhanced version, on that preset's own inputs.
|
||||
for name in vectorcraft_trace::PRESET_NAMES {
|
||||
let enhanced: &'static str = Box::leak(format!("{name}, enhanced").into_boxed_str());
|
||||
presets.extend(vectorcraft_trace::enhanced_preset(name).map(|p| (enhanced, p)));
|
||||
}
|
||||
let inputs = inputs();
|
||||
let drawn = draw_all(&inputs);
|
||||
let one_colour = |d: &&Drawing| matches!(d.0.class, Class::Shape | Class::Poly | Class::Lines | Class::Askew | Class::Plain);
|
||||
|
||||
@@ -214,21 +214,25 @@ pub struct Metrics {
|
||||
pub outside: f64,
|
||||
}
|
||||
|
||||
/// The order a grouped result is painted in: each path, then those inside it.
|
||||
/// The order a grouped result is painted in: each path together with those inside it, in the
|
||||
/// order they were traced in (as the app builds its groups).
|
||||
fn draw_order(res: &TraceResult) -> Vec<usize> {
|
||||
fn draw(i: usize, inside: &[Vec<usize>], order: &mut Vec<usize>) {
|
||||
let below = inside[i].partition_point(|&c| c < i);
|
||||
inside[i][..below].iter().for_each(|&c| draw(c, inside, order));
|
||||
order.push(i);
|
||||
inside[i][below..].iter().for_each(|&c| draw(c, inside, order));
|
||||
}
|
||||
let mut inside = vec![vec![]; res.paths.len()];
|
||||
let mut stack = vec![];
|
||||
for i in (0..res.paths.len()).rev() {
|
||||
let mut roots = vec![];
|
||||
for i in 0..res.paths.len() {
|
||||
match res.parents.get(i).copied().flatten() {
|
||||
Some(p) => inside[p].insert(0, i),
|
||||
None => stack.push(i),
|
||||
Some(p) => inside[p].push(i),
|
||||
None => roots.push(i),
|
||||
}
|
||||
}
|
||||
let mut order = vec![];
|
||||
while let Some(i) = stack.pop() {
|
||||
order.push(i);
|
||||
stack.extend(inside[i].iter().rev());
|
||||
}
|
||||
roots.into_iter().for_each(|i| draw(i, &inside, &mut order));
|
||||
order
|
||||
}
|
||||
|
||||
@@ -236,7 +240,7 @@ fn draw_order(res: &TraceResult) -> Vec<usize> {
|
||||
pub fn measure(input: &Input, variant: Variant, size: u32, src: &Raster, params: &TraceParams, res: &TraceResult) -> Metrics {
|
||||
let n = size as usize;
|
||||
let shapes: Vec<(BezPath, [u8; 3])> = res.paths.iter().map(|tp| (painted(tp), tp.color)).collect();
|
||||
// Painted as the result is grouped: each path, then those inside it.
|
||||
// Painted as the result is grouped.
|
||||
let drawn: Vec<(BezPath, [u8; 3], Option<&TracedGradient>)> =
|
||||
draw_order(res).into_iter().map(|i| (shapes[i].0.clone(), shapes[i].1, res.paths[i].gradient.as_ref())).collect();
|
||||
let (delta_mean, edge_delta) = fidelity(src, &render_painted(&drawn, [255; 3], n, n));
|
||||
|
||||
@@ -1428,6 +1428,7 @@ lighting Light Světlo
|
||||
Black and White Černobílé
|
||||
Corners Rohy
|
||||
Auto Grouping Automatické seskupení
|
||||
Enhanced Vylepšené
|
||||
Ignore Color Ignorovat barvu
|
||||
Ignore White Ignorovat bílou
|
||||
Palette: Paleta:
|
||||
@@ -4098,6 +4099,8 @@ imageTrace Outline Obrys
|
||||
@msg recovery copy `{file}`: {e} kopie pro obnovení `{file}`: {e}
|
||||
@msg resolutions must be between 1 and 2400 ppi rozlišení musí být mezi 1 a 2400 ppi
|
||||
@msg result would exceed the canvas (coordinates out of range) výsledek by přesáhl plátno (souřadnice mimo rozsah)
|
||||
@msg objects can't be nested more than 32 deep objekty nelze vnořit hlouběji než do 32 úrovní
|
||||
@msg the SVG's elements are nested more than {0} deep prvky SVG jsou vnořeny hlouběji než do {0} úrovní
|
||||
@msg scale must be a number from {_1} to {_2}, not {_3} měřítko musí být číslo od {_1} do {_2}, ne {_3}
|
||||
@msg scale must be a number from {_1} to {_2}, not {s} měřítko musí být číslo od {_1} do {_2}, ne {s}
|
||||
@msg select (or target) objects whose appearance isn't basic vyberte (nebo zacilte) objekty, jejichž vzhled není základní
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1283,6 +1283,7 @@
|
||||
Ignore Ignorieren
|
||||
Ignore Artboards (all the art as one page) Zeichenflächen ignorieren (gesamte Grafik als eine Seite)
|
||||
Auto Grouping Automatisch gruppieren
|
||||
Enhanced Erweitert
|
||||
Ignore Color Farbe ignorieren
|
||||
Ignore White Weiß ignorieren
|
||||
Palette: Palette:
|
||||
@@ -3704,6 +3705,8 @@ theme Appearance Erscheinungsbild
|
||||
@msg recovery copy `{file}`: {e} Wiederherstellungskopie `{file}`: {e}
|
||||
@msg resolutions must be between 1 and 2400 ppi Auflösungen müssen zwischen 1 und 2400 ppi liegen
|
||||
@msg result would exceed the canvas (coordinates out of range) das Ergebnis würde die Arbeitsfläche überschreiten (Koordinaten außerhalb des Bereichs)
|
||||
@msg objects can't be nested more than 32 deep Objekte können nicht tiefer als 32 Ebenen verschachtelt werden
|
||||
@msg the SVG's elements are nested more than {0} deep die Elemente der SVG-Datei sind tiefer als {0} Ebenen verschachtelt
|
||||
@msg scale must be a number from {_1} to {_2}, not {_3} scale muss eine Zahl von {_1} bis {_2} sein, nicht {_3}
|
||||
@msg scale must be a number from {_1} to {_2}, not {s} scale muss eine Zahl von {_1} bis {_2} sein, nicht {s}
|
||||
@msg select (or target) objects whose appearance isn't basic wählen Sie Objekte aus (oder als Ziel), deren Aussehen nicht einfach ist
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1301,6 +1301,7 @@
|
||||
Ignore Ignorar
|
||||
Ignore Artboards (all the art as one page) Ignorar mesas de trabajo (toda la ilustración como una página)
|
||||
Auto Grouping Agrupación automática
|
||||
Enhanced Mejorado
|
||||
Ignore Color Ignorar color
|
||||
Ignore White Ignorar blanco
|
||||
Palette: Paleta:
|
||||
@@ -3803,6 +3804,8 @@ axis Both Ambos
|
||||
@msg recovery copy `{file}`: {e} copia de recuperación `{file}`: {e}
|
||||
@msg resolutions must be between 1 and 2400 ppi las resoluciones deben estar entre 1 y 2400 ppi
|
||||
@msg result would exceed the canvas (coordinates out of range) el resultado excedería el lienzo (coordenadas fuera de rango)
|
||||
@msg objects can't be nested more than 32 deep los objetos no pueden anidarse a más de 32 niveles
|
||||
@msg the SVG's elements are nested more than {0} deep los elementos del SVG están anidados a más de {0} niveles
|
||||
@msg scale must be a number from {_1} to {_2}, not {_3} la escala debe ser un número de {_1} a {_2}, no {_3}
|
||||
@msg scale must be a number from {_1} to {_2}, not {s} la escala debe ser un número de {_1} a {_2}, no {s}
|
||||
@msg select (or target) objects whose appearance isn't basic selecciona (o apunta a) objetos cuya apariencia no sea básica
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1300,6 +1300,7 @@
|
||||
Ignore Ignorer
|
||||
Ignore Artboards (all the art as one page) Ignorer les plans de travail (toute l’illustration sur une page)
|
||||
Auto Grouping Groupement automatique
|
||||
Enhanced Amélioré
|
||||
Ignore Color Ignorer la couleur
|
||||
Ignore White Ignorer le blanc
|
||||
Palette: Palette :
|
||||
@@ -3800,6 +3801,8 @@ axis Both Les deux
|
||||
@msg recovery copy `{file}`: {e} copie de récupération `{file}` : {e}
|
||||
@msg resolutions must be between 1 and 2400 ppi les résolutions doivent être comprises entre 1 et 2400 ppp
|
||||
@msg result would exceed the canvas (coordinates out of range) le résultat dépasserait la zone de travail (coordonnées hors limites)
|
||||
@msg objects can't be nested more than 32 deep les objets ne peuvent pas être imbriqués sur plus de 32 niveaux
|
||||
@msg the SVG's elements are nested more than {0} deep les éléments du SVG sont imbriqués sur plus de {0} niveaux
|
||||
@msg scale must be a number from {_1} to {_2}, not {_3} l’échelle doit être un nombre compris entre {_1} et {_2}, pas {_3}
|
||||
@msg scale must be a number from {_1} to {_2}, not {s} l’échelle doit être un nombre compris entre {_1} et {_2}, pas {s}
|
||||
@msg select (or target) objects whose appearance isn't basic sélectionnez (ou ciblez) des objets dont l’aspect n’est pas basique
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1662,6 +1662,7 @@
|
||||
Ignore Kihagy
|
||||
Ignore Artboards (all the art as one page) Rajztáblák figyelmen kívül hagyása (az összes grafika egy oldalon)
|
||||
Auto Grouping Automatikus csoportosítás
|
||||
Enhanced Továbbfejlesztett
|
||||
Ignore Color Szín figyelmen kívül hagyása
|
||||
Ignore White Fehér figyelmen kívül hagyása
|
||||
Image Kép
|
||||
@@ -4440,6 +4441,8 @@ imageTrace Outline Körvonal
|
||||
@msg resolutions must be between 1 and 2400 ppi a felbontás 1 és 2400 ppi között lehet
|
||||
@msg Restored {_1} recovered document(s): save them to keep them Helyreállított dokumentumok: {_1}; mentsd őket, ha meg akarod tartani
|
||||
@msg result would exceed the canvas (coordinates out of range) az eredmény túllépné a vásznat (a koordináták tartományon kívül esnek)
|
||||
@msg objects can't be nested more than 32 deep az objektumok legfeljebb 32 szint mélyen ágyazhatók egymásba
|
||||
@msg the SVG's elements are nested more than {0} deep az SVG elemei több mint {0} szint mélyen vannak egymásba ágyazva
|
||||
@msg Save for Web: the image is empty Mentés webre: a kép üres
|
||||
@msg Save writes {_1}, not {_2}: use document.export for other formats a Mentés ezt írja: {_1}, ezt nem: {_2}; más formátumhoz használd a document.export parancsot
|
||||
@msg saved panel layout is invalid; reset the workspace first a mentett panelelrendezés érvénytelen; előbb állítsd vissza a munkaterületet
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1300,6 +1300,7 @@
|
||||
Ignore Ignora
|
||||
Ignore Artboards (all the art as one page) Ignora tavole da disegno (tutta la grafica come una sola pagina)
|
||||
Auto Grouping Raggruppamento automatico
|
||||
Enhanced Avanzato
|
||||
Ignore Color Ignora colore
|
||||
Ignore White Ignora bianco
|
||||
Palette: Palette:
|
||||
@@ -3803,6 +3804,8 @@ axis Both Entrambi
|
||||
@msg recovery copy `{file}`: {e} copia di recupero `{file}`: {e}
|
||||
@msg resolutions must be between 1 and 2400 ppi le risoluzioni devono essere comprese tra 1 e 2400 ppi
|
||||
@msg result would exceed the canvas (coordinates out of range) il risultato supererebbe l'area di disegno (coordinate fuori intervallo)
|
||||
@msg objects can't be nested more than 32 deep gli oggetti non possono essere nidificati oltre 32 livelli
|
||||
@msg the SVG's elements are nested more than {0} deep gli elementi dell'SVG sono nidificati oltre {0} livelli
|
||||
@msg scale must be a number from {_1} to {_2}, not {_3} la scala deve essere un numero da {_1} a {_2}, non {_3}
|
||||
@msg scale must be a number from {_1} to {_2}, not {s} la scala deve essere un numero da {_1} a {_2}, non {s}
|
||||
@msg select (or target) objects whose appearance isn't basic seleziona (o imposta come destinazione) oggetti con un aspetto non di base
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1539,6 +1539,7 @@
|
||||
Ignore 無視
|
||||
Ignore Artboards (all the art as one page) アートボードを無視 (すべてのアートを 1 ページとして扱う)
|
||||
Auto Grouping 自動グループ化
|
||||
Enhanced 拡張
|
||||
Ignore Color カラーを無視
|
||||
Ignore White 白を無視
|
||||
Palette: パレット:
|
||||
@@ -3729,6 +3730,8 @@ theme Appearance 外観
|
||||
@msg no interaction in progress 進行中の操作がありません
|
||||
@msg no such object {0} オブジェクト {0} が見つかりません
|
||||
@msg result would exceed the canvas (coordinates out of range) 結果がカンバスの範囲を超えます (座標が範囲外です)
|
||||
@msg objects can't be nested more than 32 deep オブジェクトは 32 階層より深く入れ子にできません
|
||||
@msg the SVG's elements are nested more than {0} deep SVG の要素が {0} 階層より深く入れ子になっています
|
||||
@msg unknown command `{0}` `{0}` は不明なコマンドです
|
||||
@msg `{what}` closes nothing `{what}` に対応する開始部分がありません
|
||||
@msg `{what}` closes something it didn't open `{what}` が対応しない開始部分を閉じています
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1534,6 +1534,7 @@
|
||||
Ignore 무시
|
||||
Ignore Artboards (all the art as one page) 아트보드 무시 (모든 아트웍을 한 페이지로)
|
||||
Auto Grouping 자동 그룹화
|
||||
Enhanced 향상됨
|
||||
Ignore Color 색상 무시
|
||||
Ignore White 흰색 무시
|
||||
Palette: 팔레트:
|
||||
@@ -3707,6 +3708,8 @@ theme Appearance 외관
|
||||
@msg no interaction in progress 진행 중인 상호작용이 없습니다
|
||||
@msg no such object {0} 개체 {0}이(가) 없습니다
|
||||
@msg result would exceed the canvas (coordinates out of range) 결과가 캔버스를 벗어납니다(좌표 범위 초과)
|
||||
@msg objects can't be nested more than 32 deep 개체는 32단계보다 깊게 중첩할 수 없습니다
|
||||
@msg the SVG's elements are nested more than {0} deep SVG 요소가 {0}단계보다 깊게 중첩되어 있습니다
|
||||
@msg unknown command `{0}` 알 수 없는 명령 `{0}`
|
||||
@msg `{what}` closes nothing `{what}`이(가) 닫는 항목이 없습니다
|
||||
@msg `{what}` closes something it didn't open `{what}`이(가) 열지 않은 항목을 닫습니다
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1378,6 +1378,7 @@ lighting Light Światło
|
||||
Black and White Czarno-biały
|
||||
Corners Narożniki
|
||||
Auto Grouping Automatyczne grupowanie
|
||||
Enhanced Ulepszone
|
||||
Ignore Color Ignoruj kolor
|
||||
Ignore White Ignoruj biel
|
||||
Palette: Paleta:
|
||||
@@ -3964,6 +3965,8 @@ imageTrace Outline Kontur
|
||||
@msg recovery copy `{file}`: {e} kopia odzyskiwania `{file}`: {e}
|
||||
@msg resolutions must be between 1 and 2400 ppi rozdzielczości muszą mieścić się w zakresie od 1 do 2400 ppi
|
||||
@msg result would exceed the canvas (coordinates out of range) wynik wykroczyłby poza płótno (współrzędne poza zakresem)
|
||||
@msg objects can't be nested more than 32 deep obiektów nie można zagnieżdżać głębiej niż na 32 poziomy
|
||||
@msg the SVG's elements are nested more than {0} deep elementy SVG są zagnieżdżone głębiej niż na {0} poziomów
|
||||
@msg scale must be a number from {_1} to {_2}, not {_3} skala musi być liczbą od {_1} do {_2}, nie {_3}
|
||||
@msg scale must be a number from {_1} to {_2}, not {s} skala musi być liczbą od {_1} do {_2}, nie {s}
|
||||
@msg select (or target) objects whose appearance isn't basic zaznacz (lub wskaż) obiekty, których wygląd nie jest podstawowy
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -3449,6 +3449,8 @@ axis Both Ambos
|
||||
@msg recovery copy `{file}`: {e} cópia de recuperação `{file}`: {e}
|
||||
@msg resolutions must be between 1 and 2400 ppi as resoluções devem estar entre 1 e 2400 ppi
|
||||
@msg result would exceed the canvas (coordinates out of range) o resultado excederia a tela (coordenadas fora do intervalo)
|
||||
@msg objects can't be nested more than 32 deep os objetos não podem ser aninhados em mais de 32 níveis
|
||||
@msg the SVG's elements are nested more than {0} deep os elementos do SVG estão aninhados em mais de {0} níveis
|
||||
@msg scale must be a number from {_1} to {_2}, not {_3} a escala deve ser um número de {_1} a {_2}, não {_3}
|
||||
@msg scale must be a number from {_1} to {_2}, not {s} a escala deve ser um número de {_1} a {_2}, não {s}
|
||||
@msg select (or target) objects whose appearance isn't basic selecione (ou direcione) objetos cuja aparência não seja básica
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -1298,6 +1298,7 @@
|
||||
Ignore Игнорировать
|
||||
Ignore Artboards (all the art as one page) Игнорировать монтажные области (весь рисунок как одна страница)
|
||||
Auto Grouping Автогруппировка
|
||||
Enhanced Улучшенный
|
||||
Ignore Color Игнорировать цвет
|
||||
Ignore White Игнорировать белый
|
||||
Palette: Палитра:
|
||||
@@ -3799,6 +3800,8 @@ axis Both Оба
|
||||
@msg recovery copy `{file}`: {e} копия восстановления `{file}`: {e}
|
||||
@msg resolutions must be between 1 and 2400 ppi разрешения должны быть от 1 до 2400 ppi
|
||||
@msg result would exceed the canvas (coordinates out of range) результат превысил бы холст (координаты вне диапазона)
|
||||
@msg objects can't be nested more than 32 deep объекты нельзя вкладывать глубже 32 уровней
|
||||
@msg the SVG's elements are nested more than {0} deep элементы SVG вложены глубже {0} уровней
|
||||
@msg scale must be a number from {_1} to {_2}, not {_3} масштаб должен быть числом от {_1} до {_2}, а не {_3}
|
||||
@msg scale must be a number from {_1} to {_2}, not {s} масштаб должен быть числом от {_1} до {_2}, а не {s}
|
||||
@msg select (or target) objects whose appearance isn't basic выделите (или укажите как объект) объекты, оформление которых не является основным
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1294,6 +1294,7 @@
|
||||
Ignore Ігнорувати
|
||||
Ignore Artboards (all the art as one page) Ігнорувати монтажні області (весь малюнок як одна сторінка)
|
||||
Auto Grouping Автогрупування
|
||||
Enhanced Покращений
|
||||
Ignore Color Ігнорувати колір
|
||||
Ignore White Ігнорувати білий
|
||||
Palette: Палітра:
|
||||
@@ -3787,6 +3788,8 @@ axis Both Обидві
|
||||
@msg recovery copy `{file}`: {e} копія відновлення `{file}`: {e}
|
||||
@msg resolutions must be between 1 and 2400 ppi дозволи мають бути від 1 до 2400 ppi
|
||||
@msg result would exceed the canvas (coordinates out of range) результат перевищив би полотно (координати поза діапазоном)
|
||||
@msg objects can't be nested more than 32 deep об’єкти не можна вкладати глибше ніж на 32 рівні
|
||||
@msg the SVG's elements are nested more than {0} deep елементи SVG вкладено глибше ніж на {0} рівнів
|
||||
@msg scale must be a number from {_1} to {_2}, not {_3} масштаб має бути числом від {_1} до {_2}, а не {_3}
|
||||
@msg scale must be a number from {_1} to {_2}, not {s} масштаб має бути числом від {_1} до {_2}, а не {s}
|
||||
@msg select (or target) objects whose appearance isn't basic виділіть (або вкажіть як об’єкт) об’єкти, оформлення яких не є основним
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 713 and column 40.
|
@@ -1491,6 +1491,7 @@
|
||||
Ignore Bỏ qua
|
||||
Ignore Artboards (all the art as one page) Bỏ qua bảng vẽ (toàn bộ bản vẽ thành một trang)
|
||||
Auto Grouping Tự động nhóm
|
||||
Enhanced Nâng cao
|
||||
Ignore Color Bỏ qua màu
|
||||
Ignore White Bỏ qua màu trắng
|
||||
Image Hình ảnh
|
||||
@@ -4070,6 +4071,8 @@ theme Appearance Giao diện
|
||||
@msg resolutions must be between 1 and 2400 ppi độ phân giải phải nằm trong khoảng 1 đến 2400 ppi
|
||||
@msg Restored {_1} recovered document(s): save them to keep them Đã phục hồi {_1} tài liệu: hãy lưu để giữ lại
|
||||
@msg result would exceed the canvas (coordinates out of range) kết quả sẽ vượt quá vùng vẽ (tọa độ ngoài phạm vi)
|
||||
@msg objects can't be nested more than 32 deep không thể lồng các đối tượng sâu quá 32 cấp
|
||||
@msg the SVG's elements are nested more than {0} deep các phần tử SVG lồng nhau sâu quá {0} cấp
|
||||
@msg Save for Web: the image is empty Lưu cho Web: ảnh trống
|
||||
@msg Save writes {_1}, not {_2}: use document.export for other formats Lưu ghi {_1}, không phải {_2}: dùng document.export cho các định dạng khác
|
||||
@msg saved panel layout is invalid; reset the workspace first bố cục bảng đã lưu không hợp lệ; hãy đặt lại không gian làm việc trước
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 3642 and column 10.
|
@@ -1246,6 +1246,7 @@
|
||||
Ignore 忽略
|
||||
Ignore Artboards (all the art as one page) 忽略画板 (所有图稿作为一页)
|
||||
Auto Grouping 自动编组
|
||||
Enhanced 增强
|
||||
Ignore Color 忽略颜色
|
||||
Ignore White 忽略白色
|
||||
Palette: 调板:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1276,6 +1276,7 @@
|
||||
Ignore 略過
|
||||
Ignore Artboards (all the art as one page) 忽略工作區域 (所有圖稿視為單一頁面)
|
||||
Auto Grouping 自動群組
|
||||
Enhanced 增強
|
||||
Ignore Color 忽略顏色
|
||||
Ignore White 忽略白色
|
||||
Palette: 調色盤:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -68,11 +68,12 @@ struct TraceUi {
|
||||
synced: Value,
|
||||
}
|
||||
|
||||
fn presets(app: &mut VectorcraftApp) -> Vec<(String, Value)> {
|
||||
/// Each preset: its name, its parameters, and those of its enhanced version (null if it has none).
|
||||
fn presets(app: &mut VectorcraftApp) -> Vec<(String, Value, Value)> {
|
||||
let v = app.session.execute("imageTrace.presets", &json!({})).unwrap_or_default();
|
||||
v["presets"]
|
||||
.as_array()
|
||||
.map(|a| a.iter().map(|p| (p["name"].as_str().unwrap_or("").to_string(), p["params"].clone())).collect())
|
||||
.map(|a| a.iter().map(|p| (p["name"].as_str().unwrap_or("").to_string(), p["params"].clone(), p["enhanced"].clone())).collect())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -146,7 +147,7 @@ fn preset_label(name: &str) -> &str {
|
||||
|
||||
fn preset_list(app: &mut VectorcraftApp, ui: &mut Ui, current: &str) -> Option<String> {
|
||||
let mut chosen = None;
|
||||
for (name, _) in presets(app) {
|
||||
for (name, ..) in presets(app) {
|
||||
if ui.add(egui::Button::selectable(name == current, preset_label(&name))).clicked() {
|
||||
chosen = Some(name);
|
||||
}
|
||||
@@ -242,6 +243,14 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
retrace = true;
|
||||
}
|
||||
});
|
||||
// The preset's enhanced version (with Shapes and Auto Grouping) or its legacy one.
|
||||
if let Some((_, legacy, better)) = all.iter().find(|p| p.0 == st.preset && !p.2.is_null()) {
|
||||
let on = *better == st.params;
|
||||
if widgets::check(ui, tl!("Enhanced"), on, true) {
|
||||
st.params = if on { legacy.clone() } else { better.clone() };
|
||||
retrace = true;
|
||||
}
|
||||
}
|
||||
ui.horizontal(|ui| {
|
||||
widgets::dim_label(ui, tl!("View:"));
|
||||
if let Some(v) = view_dropdown(ui, st.view, 170.0) {
|
||||
|
||||
+476
-177
@@ -1,6 +1,6 @@
|
||||
# Image Trace quality
|
||||
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-14 · **Change:** minor (stroke ends: square caps where the ink is cut square; Outline's largest distance 2.94 px to 0.88)
|
||||
> **Last reviewed:** 2026-10-14 · **Last updated:** 2026-10-14 · **Change:** minor (Gradients: grown over noise and soft rims)
|
||||
|
||||
What Image Trace (`crates/trace`) makes of images whose right answer is known, measured by a harness and
|
||||
compared with the numbers before each change. Nothing here is a judgement by eye, and nothing is compared
|
||||
@@ -52,11 +52,14 @@ The letters are drawn by hand from lines and curves: the repository has no font
|
||||
| centre off, width off, anchors a stroke | stroked centre lines (Create: Strokes) against the thin lines they trace: mean distance (px) from the true line's centre line, mean error (px) of the stroke width, anchors per stroked path |
|
||||
| shapes % | with Shapes on, the share of the plain shapes that came out as that shape (a shape, not a path, whose outline lies within 0.6 px of the true one) |
|
||||
| false shapes | with Shapes on, paths made a shape whose outline strays more than 0.6 px from the art (a shape the art does not have, or one put where it is not) |
|
||||
| grouped % | with Auto Grouping on, the share of the paths that were put inside another (the picture is painted in the grouped order for every other measure of that row, so a ΔE equal to the row without the option says grouping changed nothing that shows) |
|
||||
| grouped % | with Auto Grouping on, the share of the paths that were put inside another (the picture is painted as the app groups it for every other measure of that row: each path together with those inside it, in the order they were traced in; a ΔE equal to the row without the option says grouping changed nothing that shows) |
|
||||
| gradient paths | paths filled with a linear gradient (on inputs that are flat colour, every one would be a gradient that is not there) |
|
||||
| found %, angle off, ramp off | on the linear-gradient inputs: the share of them in which a gradient was found; and for those found, the largest angle (degrees) between the true gradient line and the traced one, and the largest difference (levels of a channel, 0 to 255) between the true colours and the traced gradient's at 17 points along the true line |
|
||||
| outside px | how far the trace reaches outside the image: a curve that strays |
|
||||
|
||||
Rows named "…, enhanced" are that preset's enhanced version (Auto Grouping on; Shapes on in all but High
|
||||
Fidelity Photo) on the same inputs as the row of the preset itself. Flat Logo has no enhanced version.
|
||||
|
||||
Black-and-white presets trace black: on the scanned variant their ΔE includes the paper's and ink's own
|
||||
colours. Silhouettes has its threshold at 200, not 128: its outline lies where the image
|
||||
crosses that tone, about 0.3 px outside the true edge on anti-aliased art and 0.8 px on blurred art; with
|
||||
@@ -85,7 +88,7 @@ Each preset on the inputs it is meant for, all sizes and variants.
|
||||
| Technical Drawing | polygons, star, letter A, lines, askew shapes and lines | 2.73 | 13.67 | 0.210 | 1.43 | 105.76 | 4.7 | 31 | 2.01 | 100 | 9 | 85 | 5.9 | – | – | – | – | 0.1 | 0.0 | – | – | – |
|
||||
| Flat Logo | two touching halves, four-colour badge | 0.43 | 3.53 | 3.106 | 88.50 | 0.78 | 69.9 | 352 | 38.92 | 38 | 10 | 80 | 237.0 | 0.0 | 0 | 13.8 | – | 59.1 | 0.2 | – | – | – |
|
||||
|
||||
### Now (with stroke caps)
|
||||
### Now (with gradients grown over noise and soft rims)
|
||||
|
||||
| preset | inputs | ΔE | edge ΔE | off mean | off max | missed max | paths | anchors | × art | lines % | upright % | corners % | kinks | odd colours | odd px | gap px | overlap px | specks | outside px | centre off | width off | anchors a stroke | shapes % | false shapes | grouped % | gradient paths | found % | angle off | ramp off |
|
||||
|:--|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|
|
||||
@@ -104,17 +107,31 @@ Each preset on the inputs it is meant for, all sizes and variants.
|
||||
| Technical Drawing | polygons, star, letter A, lines, askew shapes and lines | 2.65 | 10.88 | 0.126 | 1.46 | 105.64 | 4.7 | 38 | 2.55 | 81 | 19 | 78 | 0.8 | – | – | – | – | 0.5 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Outline | lines | 2.61 | 9.39 | 0.050 | 0.88 | 105.74 | 5.0 | 10 | 0.52 | 99 | 4 | – | – | – | – | – | – | 0.0 | 0.0 | 0.039 | 0.02 | 2.0 | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Flat Logo | two touching halves, four-colour badge | 0.48 | 3.47 | 0.090 | 0.82 | 0.78 | 3.9 | 49 | 3.85 | 40 | 10 | 80 | 2.6 | 0.0 | 0 | 10.6 | – | 0.9 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | plain shapes | 3.25 | 10.48 | 0.115 | 1.00 | 0.90 | 6.0 | 30 | 1.01 | 65 | 39 | – | – | – | – | 30.0 | 120.5 | 0.0 | 0.0 | – | – | – | 98 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | plain shapes | 0.92 | 6.89 | 0.090 | 0.99 | 0.52 | 6.0 | 30 | 1.00 | 67 | 39 | – | – | 0.0 | 0 | 23.5 | 91.2 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | plain shapes | 3.16 | 9.22 | 0.085 | 0.90 | 0.90 | 6.0 | 30 | 1.01 | 65 | 40 | – | – | – | – | 0.5 | 0.0 | 0.0 | 0.0 | – | – | – | 98 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | plain shapes | 0.90 | 6.64 | 0.077 | 0.60 | 0.60 | 6.0 | 30 | 1.00 | 67 | 39 | – | – | 0.0 | 0 | 2.1 | 1.3 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | two touching halves, four-colour badge | 0.55 | 4.99 | 0.140 | 1.34 | 1.19 | 4.0 | 49 | 3.07 | 39 | 16 | 91 | 6.1 | 0.0 | 0 | 0.3 | 0.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | 27 | 0.0 | – | – | – |
|
||||
| Shades of Gray with Auto Grouping | gradient, photo-like | 9.26 | 9.06 | – | – | – | 16.6 | 700 | – | 51 | 40 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | – | 47 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo with Auto Grouping | gradient, photo-like | 1.75 | 1.98 | – | – | – | 120.7 | 9929 | – | 38 | 36 | – | – | – | – | 0.0 | – | 10.1 | 0.0 | – | – | – | – | – | 69 | 0.0 | – | – | – |
|
||||
| 6 Colors, no Gradients | two-stop gradient, three-stop gradient in a disc | 2.12 | 3.04 | – | – | – | 6.4 | 469 | – | 21 | 14 | – | – | – | – | 3.9 | 12.7 | 0.0 | 0.0 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| 6 Colors with Gradients | two-stop gradient, three-stop gradient in a disc | 0.62 | 1.36 | – | – | – | 3.4 | 126 | – | 8 | 4 | – | – | – | – | 1.8 | 2.7 | 0.0 | 0.0 | – | – | – | – | – | – | 1.1 | 86 | 1.5 | 21 |
|
||||
| 6 Colors with Gradients | two-stop gradient, three-stop gradient in a disc | 0.62 | 1.34 | – | – | – | 1.2 | 60 | – | 0 | 0 | – | – | – | – | 1.6 | 1.2 | 0.0 | 0.0 | – | – | – | – | – | – | 0.9 | 86 | 1.5 | 21 |
|
||||
| High Fidelity Photo, no Gradients | two-stop gradient, three-stop gradient in a disc | 0.79 | 2.98 | – | – | – | 38.1 | 3827 | – | 31 | 30 | – | – | – | – | 0.0 | – | 1.1 | 0.0 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| High Fidelity Photo with Gradients | two-stop gradient, three-stop gradient in a disc | 0.46 | 1.31 | – | – | – | 20.6 | 759 | – | 9 | 5 | – | – | – | – | 0.0 | – | 2.9 | 0.0 | – | – | – | – | – | – | 1.0 | 86 | 1.5 | 21 |
|
||||
| High Fidelity Photo with Gradients | two-stop gradient, three-stop gradient in a disc | 0.46 | 1.29 | – | – | – | 8.2 | 545 | – | 1 | 0 | – | – | – | – | 0.0 | – | 0.8 | 0.0 | – | – | – | – | – | – | 0.9 | 86 | 1.5 | 21 |
|
||||
| Default, enhanced | circle, ellipses, badge with a hole, letter D, polygons, star, letter A | 2.60 | 9.88 | 0.083 | 1.00 | 2.18 | 1.9 | 16 | 1.59 | 36 | 6 | 83 | 0.4 | – | – | 0.3 | 0.5 | 0.0 | 0.0 | – | – | – | – | 0.02 | 14 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | two touching halves, four-colour badge | 0.55 | 5.00 | 0.139 | 1.33 | 1.00 | 4.0 | 48 | 3.03 | 41 | 16 | 100 | 6.0 | 0.0 | 0 | 1.3 | 1.1 | 0.0 | 0.0 | – | – | – | – | 0.00 | 27 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | gradient, photo-like | 1.75 | 1.98 | – | – | – | 120.7 | 9929 | – | 38 | 36 | – | – | – | – | 0.0 | – | 10.1 | 0.0 | – | – | – | – | – | 69 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | gradient, photo-like | 2.45 | 2.69 | – | – | – | 37.1 | 2134 | – | 43 | 37 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 63 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | two touching halves | 0.42 | 3.98 | 0.104 | 0.91 | 0.91 | 2.0 | 11 | 1.81 | 39 | 6 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | two touching halves, four-colour badge | 0.55 | 4.99 | 0.139 | 1.34 | 1.00 | 4.0 | 45 | 2.91 | 41 | 16 | 100 | 6.1 | 0.0 | 0 | 0.2 | 0.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | 27 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | two touching halves, four-colour badge | 0.55 | 4.91 | 0.141 | 1.31 | 1.00 | 4.2 | 59 | 3.68 | 42 | 17 | 100 | 7.7 | 0.0 | 0 | 0.4 | 0.5 | 0.0 | 0.0 | – | – | – | – | 0.00 | 28 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | gradient, photo-like | 9.26 | 9.06 | – | – | – | 16.6 | 700 | – | 51 | 40 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | 0.00 | 47 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | circle, ellipses, badge with a hole, letter D, polygons, star, letter A | 2.59 | 9.49 | 0.065 | 1.00 | 2.17 | 1.4 | 21 | 2.14 | 46 | 11 | 83 | 0.3 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.02 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | circle, ellipses, badge with a hole, letter D, lines | 2.71 | 11.80 | 0.178 | 1.00 | 105.61 | 1.8 | 15 | 1.32 | 19 | 8 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.03 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | circle, ellipses, badge with a hole, letter D | 2.97 | 18.98 | 0.515 | 2.19 | 2.19 | 1.2 | 13 | 1.57 | 12 | 10 | 93 | 0.4 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.11 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | lines | 2.62 | 9.50 | 0.055 | 0.99 | 105.72 | 5.4 | 42 | 2.11 | 42 | 0 | – | – | – | – | – | – | 0.1 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | polygons, star, letter A, lines, askew shapes and lines | 2.65 | 10.93 | 0.128 | 1.46 | 105.64 | 4.7 | 34 | 2.26 | 86 | 19 | 86 | 0.8 | – | – | – | – | 0.5 | 0.0 | – | – | – | – | 0.09 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | lines | 2.61 | 9.39 | 0.050 | 0.88 | 105.74 | 5.0 | 10 | 0.52 | 99 | 4 | – | – | – | – | – | – | 0.0 | 0.0 | 0.039 | 0.02 | 2.0 | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
|
||||
The sections below are the numbers now (with stroke caps).
|
||||
The sections below are the numbers now (with gradients grown over noise and soft rims).
|
||||
|
||||
## 1. Each preset on the inputs it is meant for
|
||||
|
||||
@@ -137,15 +154,29 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| Technical Drawing | polygons, star, letter A, lines, askew shapes and lines | 2.65 | 10.88 | 0.126 | 1.46 | 105.64 | 4.7 | 38 | 2.55 | 81 | 19 | 78 | 0.8 | – | – | – | – | 0.5 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Outline | lines | 2.61 | 9.39 | 0.050 | 0.88 | 105.74 | 5.0 | 10 | 0.52 | 99 | 4 | – | – | – | – | – | – | 0.0 | 0.0 | 0.039 | 0.02 | 2.0 | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Flat Logo | two touching halves, four-colour badge | 0.48 | 3.47 | 0.090 | 0.82 | 0.78 | 3.9 | 49 | 3.85 | 40 | 10 | 80 | 2.6 | 0.0 | 0 | 10.6 | – | 0.9 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | plain shapes | 3.25 | 10.48 | 0.115 | 1.00 | 0.90 | 6.0 | 30 | 1.01 | 65 | 39 | – | – | – | – | 30.0 | 120.5 | 0.0 | 0.0 | – | – | – | 98 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | plain shapes | 0.92 | 6.89 | 0.090 | 0.99 | 0.52 | 6.0 | 30 | 1.00 | 67 | 39 | – | – | 0.0 | 0 | 23.5 | 91.2 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | plain shapes | 3.16 | 9.22 | 0.085 | 0.90 | 0.90 | 6.0 | 30 | 1.01 | 65 | 40 | – | – | – | – | 0.5 | 0.0 | 0.0 | 0.0 | – | – | – | 98 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | plain shapes | 0.90 | 6.64 | 0.077 | 0.60 | 0.60 | 6.0 | 30 | 1.00 | 67 | 39 | – | – | 0.0 | 0 | 2.1 | 1.3 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | two touching halves, four-colour badge | 0.55 | 4.99 | 0.140 | 1.34 | 1.19 | 4.0 | 49 | 3.07 | 39 | 16 | 91 | 6.1 | 0.0 | 0 | 0.3 | 0.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | 27 | 0.0 | – | – | – |
|
||||
| Shades of Gray with Auto Grouping | gradient, photo-like | 9.26 | 9.06 | – | – | – | 16.6 | 700 | – | 51 | 40 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | – | 47 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo with Auto Grouping | gradient, photo-like | 1.75 | 1.98 | – | – | – | 120.7 | 9929 | – | 38 | 36 | – | – | – | – | 0.0 | – | 10.1 | 0.0 | – | – | – | – | – | 69 | 0.0 | – | – | – |
|
||||
| 6 Colors, no Gradients | two-stop gradient, three-stop gradient in a disc | 2.12 | 3.04 | – | – | – | 6.4 | 469 | – | 21 | 14 | – | – | – | – | 3.9 | 12.7 | 0.0 | 0.0 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| 6 Colors with Gradients | two-stop gradient, three-stop gradient in a disc | 0.62 | 1.36 | – | – | – | 3.4 | 126 | – | 8 | 4 | – | – | – | – | 1.8 | 2.7 | 0.0 | 0.0 | – | – | – | – | – | – | 1.1 | 86 | 1.5 | 21 |
|
||||
| 6 Colors with Gradients | two-stop gradient, three-stop gradient in a disc | 0.62 | 1.34 | – | – | – | 1.2 | 60 | – | 0 | 0 | – | – | – | – | 1.6 | 1.2 | 0.0 | 0.0 | – | – | – | – | – | – | 0.9 | 86 | 1.5 | 21 |
|
||||
| High Fidelity Photo, no Gradients | two-stop gradient, three-stop gradient in a disc | 0.79 | 2.98 | – | – | – | 38.1 | 3827 | – | 31 | 30 | – | – | – | – | 0.0 | – | 1.1 | 0.0 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| High Fidelity Photo with Gradients | two-stop gradient, three-stop gradient in a disc | 0.46 | 1.31 | – | – | – | 20.6 | 759 | – | 9 | 5 | – | – | – | – | 0.0 | – | 2.9 | 0.0 | – | – | – | – | – | – | 1.0 | 86 | 1.5 | 21 |
|
||||
| High Fidelity Photo with Gradients | two-stop gradient, three-stop gradient in a disc | 0.46 | 1.29 | – | – | – | 8.2 | 545 | – | 1 | 0 | – | – | – | – | 0.0 | – | 0.8 | 0.0 | – | – | – | – | – | – | 0.9 | 86 | 1.5 | 21 |
|
||||
| Default, enhanced | circle, ellipses, badge with a hole, letter D, polygons, star, letter A | 2.60 | 9.88 | 0.083 | 1.00 | 2.18 | 1.9 | 16 | 1.59 | 36 | 6 | 83 | 0.4 | – | – | 0.3 | 0.5 | 0.0 | 0.0 | – | – | – | – | 0.02 | 14 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | two touching halves, four-colour badge | 0.55 | 5.00 | 0.139 | 1.33 | 1.00 | 4.0 | 48 | 3.03 | 41 | 16 | 100 | 6.0 | 0.0 | 0 | 1.3 | 1.1 | 0.0 | 0.0 | – | – | – | – | 0.00 | 27 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | gradient, photo-like | 1.75 | 1.98 | – | – | – | 120.7 | 9929 | – | 38 | 36 | – | – | – | – | 0.0 | – | 10.1 | 0.0 | – | – | – | – | – | 69 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | gradient, photo-like | 2.45 | 2.69 | – | – | – | 37.1 | 2134 | – | 43 | 37 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 63 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | two touching halves | 0.42 | 3.98 | 0.104 | 0.91 | 0.91 | 2.0 | 11 | 1.81 | 39 | 6 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | two touching halves, four-colour badge | 0.55 | 4.99 | 0.139 | 1.34 | 1.00 | 4.0 | 45 | 2.91 | 41 | 16 | 100 | 6.1 | 0.0 | 0 | 0.2 | 0.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | 27 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | two touching halves, four-colour badge | 0.55 | 4.91 | 0.141 | 1.31 | 1.00 | 4.2 | 59 | 3.68 | 42 | 17 | 100 | 7.7 | 0.0 | 0 | 0.4 | 0.5 | 0.0 | 0.0 | – | – | – | – | 0.00 | 28 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | gradient, photo-like | 9.26 | 9.06 | – | – | – | 16.6 | 700 | – | 51 | 40 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | 0.00 | 47 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | circle, ellipses, badge with a hole, letter D, polygons, star, letter A | 2.59 | 9.49 | 0.065 | 1.00 | 2.17 | 1.4 | 21 | 2.14 | 46 | 11 | 83 | 0.3 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.02 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | circle, ellipses, badge with a hole, letter D, lines | 2.71 | 11.80 | 0.178 | 1.00 | 105.61 | 1.8 | 15 | 1.32 | 19 | 8 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.03 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | circle, ellipses, badge with a hole, letter D | 2.97 | 18.98 | 0.515 | 2.19 | 2.19 | 1.2 | 13 | 1.57 | 12 | 10 | 93 | 0.4 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.11 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | lines | 2.62 | 9.50 | 0.055 | 0.99 | 105.72 | 5.4 | 42 | 2.11 | 42 | 0 | – | – | – | – | – | – | 0.1 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | polygons, star, letter A, lines, askew shapes and lines | 2.65 | 10.93 | 0.128 | 1.46 | 105.64 | 4.7 | 34 | 2.26 | 86 | 19 | 86 | 0.8 | – | – | – | – | 0.5 | 0.0 | – | – | – | – | 0.09 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | lines | 2.61 | 9.39 | 0.050 | 0.88 | 105.74 | 5.0 | 10 | 0.52 | 99 | 4 | – | – | – | – | – | – | 0.0 | 0.0 | 0.039 | 0.02 | 2.0 | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
|
||||
## 2. Each preset on each kind of input (clean, three sizes)
|
||||
|
||||
@@ -271,24 +302,24 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| Flat Logo | flat colour art | 0.07 | 1.53 | 0.065 | 0.31 | 0.27 | 3.0 | 36 | 3.51 | 47 | 11 | 100 | 0.0 | 0.0 | 0 | 6.8 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Flat Logo | gradient, photo | refused | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|
||||
| Flat Logo | linear gradients | 6.49 | 6.47 | – | – | – | 484.0 | 4773 | – | 58 | 0 | – | – | – | – | 146.0 | – | 446.7 | 0.1 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| Default with Shapes | curved shapes | 0.08 | 3.19 | 0.058 | 0.38 | 0.38 | 1.8 | 13 | 1.44 | 14 | 14 | 100 | 0.0 | – | – | 12.2 | 7.4 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | polygons | 0.07 | 2.63 | 0.043 | 0.50 | 1.26 | 2.0 | 13 | 1.16 | 97 | 5 | 100 | 0.0 | – | – | 5.6 | 7.8 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | thin lines | 0.54 | 4.53 | 0.049 | 0.52 | 105.72 | 3.3 | 25 | 1.27 | 40 | 0 | – | – | – | – | 0.0 | 18.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | askew shapes | 0.17 | 3.85 | 0.050 | 0.37 | 36.06 | 3.7 | 23 | 0.94 | 100 | 3 | – | – | – | – | 0.0 | 42.1 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | plain shapes | 0.19 | 3.95 | 0.067 | 0.43 | 0.34 | 6.0 | 30 | 1.00 | 66 | 54 | – | – | – | – | 25.1 | 75.9 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | flat colour art | 19.91 | 17.91 | 0.176 | 0.51 | 179.34 | 1.5 | 16 | 1.01 | 14 | 14 | 0 | 0.0 | – | – | 14.0 | 7.5 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | curved shapes | 0.08 | 3.21 | 0.056 | 0.38 | 0.38 | 1.8 | 13 | 1.44 | 14 | 14 | 100 | 0.0 | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | polygons | 0.07 | 2.58 | 0.045 | 0.50 | 1.26 | 2.0 | 13 | 1.16 | 97 | 5 | 100 | 0.0 | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | thin lines | 0.54 | 4.50 | 0.049 | 0.47 | 105.72 | 3.3 | 25 | 1.27 | 40 | 0 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | askew shapes | 0.16 | 3.24 | 0.041 | 0.37 | 36.06 | 3.7 | 23 | 0.94 | 100 | 3 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | plain shapes | 0.11 | 2.30 | 0.049 | 0.28 | 0.28 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | flat colour art | 19.91 | 17.94 | 0.180 | 0.39 | 179.45 | 1.5 | 16 | 1.01 | 14 | 14 | 0 | 0.0 | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | gradient, photo | 39.03 | 38.64 | – | – | – | 3.2 | 595 | – | 55 | 41 | – | – | – | – | 18.8 | 35.5 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | linear gradients | 25.64 | 29.86 | – | – | – | 2.0 | 43 | – | 33 | 32 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | 0 | – | – |
|
||||
| 6 Colors with Shapes | curved shapes | 0.05 | 2.24 | 0.041 | 0.27 | 0.28 | 1.8 | 15 | 1.59 | 16 | 16 | 100 | 0.0 | 0.0 | 0 | 9.7 | 5.1 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | polygons | 0.05 | 2.24 | 0.039 | 0.42 | 1.39 | 2.0 | 14 | 1.27 | 94 | 7 | 99 | 0.0 | 0.0 | 0 | 2.1 | 8.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | thin lines | 0.49 | 4.78 | 0.108 | 0.70 | 45.31 | 4.0 | 30 | 1.50 | 63 | 19 | – | – | 1.7 | 413 | 0.0 | 10.8 | 0.0 | 0.0 | – | – | – | – | 0.33 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | askew shapes | 0.20 | 4.24 | 0.064 | 0.73 | 36.06 | 4.0 | 25 | 1.03 | 99 | 3 | – | – | 0.3 | 11 | 0.0 | 49.7 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | plain shapes | 0.16 | 3.03 | 0.050 | 0.34 | 0.24 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 15.5 | 43.8 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | flat colour art | 0.13 | 2.71 | 0.057 | 0.31 | 0.30 | 3.0 | 28 | 2.00 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.5 | 9.1 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | gradient, photo | 5.00 | 5.27 | – | – | – | 13.0 | 828 | – | 37 | 28 | – | – | – | – | 7.3 | 13.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | curved shapes | 0.05 | 2.25 | 0.039 | 0.27 | 0.28 | 1.8 | 15 | 1.59 | 16 | 16 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | polygons | 0.06 | 2.28 | 0.042 | 0.63 | 1.39 | 2.0 | 14 | 1.27 | 94 | 7 | 99 | 0.0 | 0.0 | 0 | 2.0 | 2.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | thin lines | 0.49 | 4.79 | 0.108 | 0.70 | 45.31 | 4.0 | 30 | 1.50 | 63 | 19 | – | – | 1.7 | 413 | 8.1 | 2.3 | 0.0 | 0.0 | – | – | – | – | 0.33 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | askew shapes | 0.18 | 3.59 | 0.053 | 0.73 | 36.06 | 4.0 | 25 | 1.03 | 99 | 3 | – | – | 0.3 | 11 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | plain shapes | 0.09 | 1.88 | 0.039 | 0.29 | 0.29 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | flat colour art | 0.13 | 2.72 | 0.057 | 0.31 | 0.30 | 3.0 | 28 | 2.00 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | gradient, photo | 5.00 | 5.28 | – | – | – | 13.0 | 828 | – | 37 | 28 | – | – | – | – | 7.5 | 10.6 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | linear gradients | 2.07 | 3.07 | – | – | – | 6.2 | 266 | – | 20 | 14 | – | – | – | – | 2.4 | 9.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | 0 | – | – |
|
||||
| 6 Colors with Auto Grouping | curved shapes | 0.08 | 3.46 | 0.043 | 0.27 | 0.28 | 1.8 | 16 | 1.77 | 16 | 16 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 17 | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | polygons | 0.07 | 2.87 | 0.041 | 0.42 | 1.39 | 2.0 | 16 | 1.45 | 86 | 7 | 93 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 11 | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | curved shapes | 0.06 | 2.43 | 0.043 | 0.27 | 0.28 | 1.8 | 16 | 1.77 | 16 | 16 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 17 | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | polygons | 0.06 | 2.40 | 0.041 | 0.42 | 1.39 | 2.0 | 16 | 1.45 | 86 | 7 | 93 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 11 | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | thin lines | 0.49 | 4.76 | 0.108 | 0.70 | 45.31 | 4.0 | 30 | 1.50 | 63 | 19 | – | – | 1.7 | 413 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | askew shapes | 0.19 | 3.72 | 0.058 | 0.73 | 36.06 | 4.0 | 25 | 1.06 | 90 | 3 | – | – | 0.3 | 11 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | plain shapes | 0.10 | 2.07 | 0.043 | 0.30 | 0.26 | 6.0 | 42 | 1.41 | 59 | 49 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 0 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
@@ -325,7 +356,7 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| 6 Colors with Gradients | askew shapes | 0.19 | 3.72 | 0.058 | 0.73 | 36.06 | 4.0 | 25 | 1.06 | 90 | 3 | – | – | 0.3 | 11 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Gradients | plain shapes | 0.10 | 2.07 | 0.043 | 0.30 | 0.26 | 6.0 | 42 | 1.41 | 59 | 49 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 0 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Gradients | flat colour art | 0.13 | 2.72 | 0.057 | 0.31 | 0.30 | 3.0 | 28 | 2.00 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Gradients | gradient, photo | 4.52 | 4.86 | – | – | – | 16.3 | 919 | – | 36 | 26 | – | – | – | – | 8.8 | 12.1 | 0.0 | 0.0 | – | – | – | – | – | – | 0.7 | – | – | – |
|
||||
| 6 Colors with Gradients | gradient, photo | 4.49 | 4.84 | – | – | – | 15.0 | 909 | – | 36 | 27 | – | – | – | – | 9.7 | 12.0 | 0.0 | 0.0 | – | – | – | – | – | – | 0.7 | – | – | – |
|
||||
| 6 Colors with Gradients | linear gradients | 0.40 | 1.09 | – | – | – | 1.3 | 39 | – | 0 | 0 | – | – | – | – | 0.5 | 1.1 | 0.0 | 0.0 | – | – | – | – | – | – | 0.8 | 83 | 0.2 | 5 |
|
||||
| High Fidelity Photo, no Gradients | curved shapes | 0.04 | 1.63 | 0.026 | 0.15 | 0.15 | 1.2 | 19 | 2.11 | 21 | 21 | 100 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, no Gradients | polygons | 0.06 | 2.33 | 0.039 | 0.50 | 1.32 | 1.7 | 17 | 1.47 | 88 | 7 | 93 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
@@ -341,8 +372,120 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| High Fidelity Photo with Gradients | askew shapes | 0.17 | 3.97 | 0.100 | 0.94 | 1.73 | 8.7 | 157 | 6.54 | 71 | 5 | – | – | 4.3 | 3998 | 0.0 | – | 1.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| High Fidelity Photo with Gradients | plain shapes | 0.08 | 1.66 | 0.035 | 0.25 | 0.28 | 6.0 | 50 | 1.68 | 62 | 51 | – | – | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | 0 | 0.00 | – | 0.0 | – | – | – |
|
||||
| High Fidelity Photo with Gradients | flat colour art | 0.14 | 4.16 | 0.029 | 0.20 | 0.20 | 3.5 | 73 | 4.59 | 35 | 34 | 100 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| High Fidelity Photo with Gradients | gradient, photo | 1.80 | 2.04 | – | – | – | 140.7 | 10844 | – | 41 | 39 | – | – | – | – | 0.0 | – | 10.0 | 0.0 | – | – | – | – | – | – | 0.8 | – | – | – |
|
||||
| High Fidelity Photo with Gradients | gradient, photo | 1.79 | 2.04 | – | – | – | 138.0 | 11156 | – | 40 | 39 | – | – | – | – | 0.0 | – | 9.2 | 0.0 | – | – | – | – | – | – | 0.8 | – | – | – |
|
||||
| High Fidelity Photo with Gradients | linear gradients | 0.23 | 1.19 | – | – | – | 6.0 | 252 | – | 0 | 0 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | – | – | 0.8 | 83 | 0.2 | 5 |
|
||||
| Default, enhanced | curved shapes | 0.08 | 3.21 | 0.056 | 0.38 | 0.38 | 1.8 | 13 | 1.44 | 14 | 14 | 100 | 0.0 | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 17 | 0.0 | – | – | – |
|
||||
| Default, enhanced | polygons | 0.07 | 2.58 | 0.045 | 0.50 | 1.26 | 2.0 | 13 | 1.16 | 97 | 5 | 100 | 0.0 | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 11 | 0.0 | – | – | – |
|
||||
| Default, enhanced | thin lines | 0.54 | 4.50 | 0.049 | 0.47 | 105.72 | 3.3 | 25 | 1.27 | 40 | 0 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Default, enhanced | askew shapes | 0.16 | 3.24 | 0.041 | 0.37 | 36.06 | 3.7 | 23 | 0.94 | 100 | 3 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Default, enhanced | plain shapes | 0.11 | 2.30 | 0.049 | 0.28 | 0.28 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Default, enhanced | flat colour art | 19.91 | 17.94 | 0.180 | 0.39 | 179.45 | 1.5 | 16 | 1.01 | 14 | 14 | 0 | 0.0 | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 17 | 0.0 | – | – | – |
|
||||
| Default, enhanced | gradient, photo | 39.03 | 38.64 | – | – | – | 3.2 | 595 | – | 55 | 41 | – | – | – | – | 18.8 | 35.5 | 0.0 | 0.0 | – | – | – | – | 0.00 | 15 | 0.0 | – | – | – |
|
||||
| Default, enhanced | linear gradients | 25.64 | 29.86 | – | – | – | 2.0 | 43 | – | 33 | 32 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| Auto Color, enhanced | curved shapes | 0.05 | 2.25 | 0.039 | 0.27 | 0.28 | 1.8 | 15 | 1.59 | 16 | 16 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 17 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | polygons | 0.06 | 2.30 | 0.042 | 0.63 | 1.39 | 2.0 | 14 | 1.25 | 95 | 7 | 99 | 0.0 | 0.0 | 0 | 2.0 | 2.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 11 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | thin lines | 0.70 | 5.57 | 0.067 | 1.08 | 105.72 | 5.0 | 38 | 1.88 | 43 | 3 | – | – | 4.0 | 3225 | 5.2 | 4.6 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | askew shapes | 0.64 | 3.72 | 0.044 | 0.36 | 9.49 | 4.0 | 24 | 1.00 | 100 | 3 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | plain shapes | 0.09 | 1.88 | 0.039 | 0.29 | 0.29 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | flat colour art | 0.13 | 2.72 | 0.057 | 0.31 | 0.30 | 3.0 | 28 | 2.00 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | gradient, photo | 2.08 | 2.39 | – | – | – | 159.3 | 4439 | – | 22 | 9 | – | – | – | – | 41.7 | 51.9 | 0.0 | 0.5 | – | – | – | – | 0.00 | 8 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | linear gradients | 1.02 | 1.74 | – | – | – | 15.7 | 1272 | – | 12 | 8 | – | – | – | – | 9.1 | 7.4 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| High Fidelity Photo, enhanced | curved shapes | 0.04 | 1.63 | 0.026 | 0.15 | 0.15 | 1.2 | 19 | 2.11 | 21 | 21 | 100 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | polygons | 0.06 | 2.33 | 0.039 | 0.50 | 1.32 | 1.7 | 17 | 1.47 | 88 | 7 | 93 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | thin lines | 0.35 | 6.05 | 0.085 | 0.90 | 4.35 | 40.3 | 348 | 17.42 | 35 | 9 | – | – | 27.3 | 4505 | 0.0 | – | 19.0 | 0.0 | – | – | – | – | 0.00 | 23 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | askew shapes | 0.17 | 3.97 | 0.100 | 0.94 | 1.73 | 8.7 | 157 | 6.54 | 71 | 5 | – | – | 4.3 | 3998 | 0.0 | – | 1.0 | 0.0 | – | – | – | – | 0.00 | 18 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | plain shapes | 0.08 | 1.66 | 0.035 | 0.25 | 0.28 | 6.0 | 50 | 1.68 | 62 | 51 | – | – | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | 0 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | flat colour art | 0.14 | 4.16 | 0.029 | 0.20 | 0.20 | 3.5 | 73 | 4.59 | 35 | 34 | 100 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 25 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | gradient, photo | 1.80 | 2.05 | – | – | – | 134.7 | 11212 | – | 39 | 37 | – | – | – | – | 0.0 | – | 7.3 | 0.0 | – | – | – | – | – | 69 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | linear gradients | 0.64 | 3.29 | – | – | – | 34.5 | 4318 | – | 32 | 31 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | – | 72 | 0.0 | 0 | – | – |
|
||||
| Low Fidelity Photo, enhanced | curved shapes | 0.06 | 2.52 | 0.043 | 0.31 | 0.31 | 1.2 | 12 | 1.29 | 18 | 18 | 100 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.00 | 0 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | polygons | 0.06 | 2.34 | 0.039 | 0.42 | 1.39 | 1.7 | 12 | 1.13 | 95 | 7 | 99 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.00 | 0 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | thin lines | 0.49 | 5.73 | 0.082 | 0.68 | 45.31 | 7.0 | 53 | 2.63 | 53 | 11 | – | – | 4.0 | 3989 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.67 | 0 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | askew shapes | 0.23 | 4.54 | 0.055 | 0.70 | 21.55 | 6.0 | 40 | 1.68 | 94 | 3 | – | – | 2.3 | 2630 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.00 | 3 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | plain shapes | 0.10 | 2.02 | 0.042 | 0.27 | 0.27 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | 100 | 1.00 | 0 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | flat colour art | 0.15 | 4.19 | 0.042 | 0.28 | 0.20 | 3.5 | 48 | 2.93 | 32 | 31 | 100 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.00 | 25 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | gradient, photo | 2.53 | 2.80 | – | – | – | 42.7 | 2307 | – | 43 | 37 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 60 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | linear gradients | 0.69 | 3.09 | – | – | – | 24.3 | 1045 | – | 31 | 31 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 79 | 0.0 | 0 | – | – |
|
||||
| 3 Colors, enhanced | curved shapes | 0.06 | 2.52 | 0.043 | 0.31 | 0.31 | 1.8 | 14 | 1.53 | 15 | 15 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 17 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | polygons | 0.06 | 2.34 | 0.043 | 0.63 | 1.39 | 2.0 | 14 | 1.26 | 94 | 7 | 99 | 0.0 | 0.0 | 0 | 2.0 | 2.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 11 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | thin lines | 0.43 | 4.49 | 0.117 | 1.04 | 45.31 | 5.0 | 41 | 2.05 | 57 | 13 | – | – | 2.0 | 308 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.33 | 0 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | askew shapes | 0.18 | 3.53 | 0.052 | 0.70 | 14.59 | 4.0 | 25 | 1.04 | 97 | 3 | – | – | 0.7 | 23 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | plain shapes | 0.10 | 2.02 | 0.042 | 0.27 | 0.27 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | flat colour art | 1.31 | 3.58 | 0.124 | 0.90 | 60.11 | 2.5 | 23 | 1.77 | 45 | 20 | 100 | 0.0 | 1.0 | 9912 | 9.0 | 9.0 | 0.0 | 0.0 | – | – | – | – | 0.33 | 25 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | gradient, photo | 7.49 | 7.60 | – | – | – | 5.7 | 524 | – | 48 | 40 | – | – | – | – | 7.0 | 8.8 | 0.0 | 0.0 | – | – | – | – | 0.00 | 20 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | linear gradients | 4.45 | 5.79 | – | – | – | 3.0 | 78 | – | 28 | 24 | – | – | – | – | 10.3 | 1.9 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| 6 Colors, enhanced | curved shapes | 0.05 | 2.25 | 0.039 | 0.27 | 0.28 | 1.8 | 15 | 1.59 | 16 | 16 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 17 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | polygons | 0.06 | 2.28 | 0.042 | 0.63 | 1.39 | 2.0 | 14 | 1.27 | 94 | 7 | 99 | 0.0 | 0.0 | 0 | 2.0 | 2.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 11 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | thin lines | 0.49 | 4.79 | 0.108 | 0.70 | 45.31 | 4.0 | 30 | 1.50 | 63 | 19 | – | – | 1.7 | 413 | 8.1 | 2.3 | 0.0 | 0.0 | – | – | – | – | 0.33 | 0 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | askew shapes | 0.18 | 3.59 | 0.053 | 0.73 | 36.06 | 4.0 | 25 | 1.03 | 99 | 3 | – | – | 0.3 | 11 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | plain shapes | 0.09 | 1.88 | 0.039 | 0.29 | 0.29 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | flat colour art | 0.13 | 2.72 | 0.057 | 0.31 | 0.30 | 3.0 | 28 | 2.00 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | gradient, photo | 5.00 | 5.28 | – | – | – | 13.0 | 828 | – | 37 | 28 | – | – | – | – | 7.5 | 10.6 | 0.0 | 0.0 | – | – | – | – | 0.00 | 13 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | linear gradients | 2.07 | 3.07 | – | – | – | 6.2 | 266 | – | 20 | 14 | – | – | – | – | 2.4 | 9.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| 16 Colors, enhanced | curved shapes | 0.05 | 2.06 | 0.034 | 0.24 | 0.25 | 1.8 | 16 | 1.64 | 16 | 16 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 17 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | polygons | 0.05 | 2.22 | 0.041 | 0.63 | 1.39 | 2.0 | 14 | 1.28 | 94 | 7 | 99 | 0.0 | 0.0 | 0 | 2.0 | 2.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 11 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | thin lines | 0.39 | 3.93 | 0.113 | 0.70 | 23.53 | 8.7 | 52 | 2.58 | 56 | 17 | – | – | 5.7 | 322 | 8.1 | 2.3 | 0.0 | 0.0 | – | – | – | – | 0.33 | 0 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | askew shapes | 0.17 | 3.52 | 0.058 | 0.73 | 21.55 | 4.3 | 27 | 1.14 | 95 | 3 | – | – | 0.7 | 17 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | plain shapes | 0.08 | 1.70 | 0.035 | 0.13 | 0.13 | 6.0 | 30 | 1.00 | 67 | 55 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | flat colour art | 0.12 | 2.53 | 0.052 | 0.31 | 0.30 | 3.0 | 28 | 2.11 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | gradient, photo | 3.00 | 3.30 | – | – | – | 47.2 | 2126 | – | 30 | 16 | – | – | – | – | 15.2 | 19.6 | 0.0 | 0.0 | – | – | – | – | 0.00 | 15 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | linear gradients | 0.98 | 1.67 | – | – | – | 15.8 | 1340 | – | 8 | 6 | – | – | – | – | 13.9 | 15.5 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| Shades of Gray, enhanced | curved shapes | 0.06 | 2.53 | 0.044 | 0.31 | 0.31 | 1.2 | 12 | 1.29 | 18 | 18 | 100 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.00 | 0 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | polygons | 0.07 | 2.49 | 0.042 | 0.50 | 1.26 | 1.7 | 11 | 1.03 | 99 | 5 | 100 | 0.0 | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.00 | 0 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | thin lines | 0.56 | 7.12 | 0.089 | 0.68 | 45.31 | 10.3 | 73 | 3.65 | 55 | 11 | – | – | 8.0 | 4322 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.00 | 0 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | askew shapes | 0.25 | 4.88 | 0.046 | 0.73 | 21.52 | 6.0 | 39 | 1.61 | 95 | 3 | – | – | 2.3 | 2638 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.00 | 3 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | plain shapes | 0.09 | 1.97 | 0.042 | 0.25 | 0.25 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 0.0 | – | 0.0 | 0.0 | – | – | – | 100 | 1.00 | 0 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | flat colour art | 7.33 | 11.09 | 0.244 | 1.47 | 179.46 | 4.7 | 161 | 13.44 | 24 | 24 | 50 | 0.0 | 3.7 | 173377 | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 1.33 | 36 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | gradient, photo | 9.71 | 9.55 | – | – | – | 18.3 | 753 | – | 51 | 41 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | 0.00 | 53 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | linear gradients | 5.25 | 8.40 | – | – | – | 9.0 | 474 | – | 31 | 31 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 63 | 0.0 | 0 | – | – |
|
||||
| Black and White Logo, enhanced | curved shapes | 0.03 | 1.46 | 0.022 | 0.16 | 0.16 | 1.2 | 16 | 1.67 | 21 | 21 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | polygons | 0.07 | 2.68 | 0.046 | 0.50 | 1.26 | 1.7 | 11 | 1.03 | 99 | 8 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | thin lines | 0.60 | 6.03 | 0.105 | 0.50 | 77.21 | 10.3 | 44 | 2.18 | 90 | 10 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | askew shapes | 0.49 | 11.08 | 0.232 | 1.46 | 9.63 | 4.0 | 25 | 1.03 | 98 | 70 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.67 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | plain shapes | 0.08 | 1.73 | 0.033 | 0.24 | 0.24 | 6.0 | 30 | 1.00 | 67 | 55 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | 100 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | flat colour art | 19.90 | 17.76 | 0.171 | 0.30 | 179.47 | 1.0 | 18 | 1.12 | 23 | 23 | 0 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | gradient, photo | 38.97 | 38.53 | – | – | – | 9.0 | 634 | – | 73 | 54 | – | – | – | – | – | – | 1.2 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | linear gradients | 25.62 | 29.78 | – | – | – | 1.5 | 127 | – | 46 | 33 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| Sketched Art, enhanced | curved shapes | 0.13 | 5.38 | 0.103 | 0.46 | 0.46 | 1.2 | 11 | 1.24 | 16 | 16 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | polygons | 0.13 | 5.02 | 0.090 | 0.49 | 1.08 | 1.7 | 12 | 1.07 | 94 | 5 | 98 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | thin lines | 0.47 | 5.72 | 0.114 | 0.96 | 52.59 | 4.3 | 29 | 1.45 | 64 | 3 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.33 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | askew shapes | 0.32 | 6.06 | 0.092 | 0.45 | 35.94 | 3.3 | 23 | 0.94 | 96 | 4 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | plain shapes | 0.31 | 5.96 | 0.105 | 0.25 | 0.25 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | 100 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | flat colour art | 20.24 | 19.33 | 0.158 | 0.83 | 179.59 | 1.5 | 15 | 1.00 | 16 | 16 | 0 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.50 | 25 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | gradient, photo | 45.42 | 45.21 | – | – | – | 1.0 | 244 | – | 46 | 35 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | linear gradients | 29.20 | 33.47 | – | – | – | 1.5 | 54 | – | 35 | 34 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| Silhouettes, enhanced | curved shapes | 0.35 | 14.33 | 0.277 | 0.69 | 0.69 | 1.2 | 11 | 1.24 | 16 | 16 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | polygons | 0.35 | 13.66 | 0.255 | 0.74 | 1.67 | 1.7 | 13 | 1.20 | 82 | 6 | 93 | 0.1 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.11 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | thin lines | 0.99 | 13.79 | 0.267 | 1.08 | 18.26 | 5.0 | 30 | 1.50 | 67 | 12 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 1.00 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | askew shapes | 0.58 | 14.94 | 0.298 | 0.57 | 0.55 | 4.0 | 24 | 1.01 | 95 | 3 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | plain shapes | 0.76 | 15.32 | 0.293 | 0.63 | 0.63 | 6.0 | 30 | 1.00 | 67 | 50 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | 94 | 0.33 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | flat colour art | 23.82 | 32.43 | 0.189 | 0.35 | 188.27 | 1.0 | 6 | 0.53 | 37 | 37 | 0 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | gradient, photo | 61.57 | 61.32 | – | – | – | 0.2 | 2 | – | 47 | 21 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | linear gradients | 39.85 | 46.48 | – | – | – | 1.0 | 37 | – | 1 | 0 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| Line Art, enhanced | curved shapes | 0.04 | 1.76 | 0.029 | 0.22 | 0.22 | 1.2 | 14 | 1.46 | 20 | 20 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | polygons | 0.07 | 2.38 | 0.040 | 0.50 | 1.26 | 1.7 | 11 | 1.04 | 98 | 5 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | thin lines | 0.52 | 4.12 | 0.040 | 0.47 | 105.72 | 3.3 | 29 | 1.45 | 40 | 0 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | askew shapes | 0.12 | 3.08 | 0.042 | 0.48 | 9.60 | 4.0 | 24 | 1.01 | 98 | 3 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | plain shapes | 0.07 | 1.55 | 0.032 | 0.13 | 0.13 | 6.0 | 30 | 1.00 | 67 | 55 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | 100 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | flat colour art | 19.90 | 17.84 | 0.168 | 0.30 | 179.50 | 1.0 | 16 | 1.03 | 21 | 21 | 0 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | gradient, photo | 38.98 | 38.54 | – | – | – | 7.7 | 472 | – | 58 | 44 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | linear gradients | 25.63 | 29.80 | – | – | – | 1.5 | 95 | – | 33 | 32 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| Technical Drawing, enhanced | curved shapes | 0.03 | 1.46 | 0.022 | 0.16 | 0.16 | 1.2 | 16 | 1.67 | 21 | 21 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | polygons | 0.07 | 2.68 | 0.046 | 0.50 | 1.26 | 1.7 | 11 | 1.02 | 100 | 8 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | thin lines | 0.50 | 5.50 | 0.110 | 0.50 | 55.93 | 15.3 | 62 | 3.12 | 86 | 14 | – | – | – | – | – | – | 3.3 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | askew shapes | 0.48 | 11.01 | 0.230 | 1.46 | 2.11 | 4.3 | 26 | 1.08 | 98 | 69 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.67 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | plain shapes | 0.08 | 1.81 | 0.035 | 0.24 | 0.24 | 6.0 | 30 | 1.00 | 67 | 55 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | 100 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | flat colour art | 19.90 | 17.76 | 0.171 | 0.30 | 179.47 | 1.0 | 18 | 1.12 | 23 | 23 | 0 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | gradient, photo | 38.94 | 38.51 | – | – | – | 20.0 | 659 | – | 73 | 51 | – | – | – | – | – | – | 11.7 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | linear gradients | 25.62 | 29.80 | – | – | – | 1.5 | 118 | – | 49 | 33 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
| Outline, enhanced | curved shapes | 23.38 | 44.76 | 0.499 | 0.71 | 0.52 | 1.2 | 16 | 1.70 | 20 | 20 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | polygons | 17.94 | 44.26 | 0.494 | 0.94 | 0.76 | 1.7 | 14 | 1.26 | 89 | 5 | 95 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | thin lines | 0.51 | 3.57 | 0.028 | 0.41 | 105.74 | 3.3 | 7 | 0.33 | 100 | 7 | – | – | – | – | – | – | 0.0 | 0.0 | 0.020 | 0.02 | 2.0 | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | askew shapes | 23.68 | 36.53 | 0.422 | 0.62 | 6.62 | 4.0 | 20 | 0.83 | 100 | 3 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | plain shapes | 34.32 | 43.23 | 0.495 | 0.73 | 0.51 | 6.0 | 47 | 1.56 | 56 | 51 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | 0 | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | flat colour art | 26.69 | 32.18 | 0.494 | 0.92 | 178.93 | 1.0 | 18 | 1.30 | 21 | 21 | 0 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | gradient, photo | 39.38 | 39.18 | – | – | – | 7.5 | 445 | – | 59 | 43 | – | – | – | – | – | – | 3.3 | 0.5 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | linear gradients | 25.08 | 30.80 | – | – | – | 1.5 | 92 | – | 33 | 32 | – | – | – | – | – | – | 0.0 | 0.5 | – | – | – | – | 0.00 | 0 | 0.0 | 0 | – | – |
|
||||
|
||||
## 3. Each preset on its inputs, by variant (256 px)
|
||||
|
||||
@@ -423,16 +566,16 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| Flat Logo | blurred | 0.54 | 8.33 | 0.052 | 0.62 | 0.78 | 3.0 | 28 | 1.78 | 43 | 16 | 50 | 0.0 | 0.0 | 0 | 5.8 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Flat Logo | noisy | 0.81 | 1.77 | 0.076 | 0.39 | 0.32 | 3.5 | 44 | 2.63 | 31 | 0 | 100 | 1.0 | 0.0 | 0 | 6.9 | – | 0.5 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Flat Logo | scanned | 1.68 | 3.76 | 0.088 | 0.82 | 0.66 | 8.5 | 128 | 10.04 | 23 | 0 | 62 | 17.0 | 0.0 | 0 | 35.1 | – | 5.5 | 0.0 | – | – | – | – | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | clean | 0.19 | 3.95 | 0.067 | 0.43 | 0.34 | 6.0 | 30 | 1.00 | 66 | 54 | – | – | – | – | 25.1 | 75.9 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | aliased | 0.54 | 14.09 | 0.238 | 1.00 | 0.90 | 6.0 | 33 | 1.10 | 56 | 43 | – | – | – | – | 58.1 | 211.4 | 0.0 | 0.0 | – | – | – | 83 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | blurred | 1.72 | 17.70 | 0.123 | 0.81 | 0.32 | 6.0 | 30 | 1.00 | 66 | 54 | – | – | – | – | 29.7 | 146.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | noisy | 2.70 | 5.97 | 0.074 | 0.38 | 0.32 | 6.0 | 30 | 1.00 | 67 | 0 | – | – | – | – | 16.1 | 79.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | scanned | 17.22 | 23.75 | 0.165 | 0.52 | 0.46 | 6.0 | 30 | 1.00 | 66 | 17 | – | – | – | – | 30.5 | 179.7 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | clean | 0.16 | 3.03 | 0.050 | 0.34 | 0.24 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 15.5 | 43.8 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | aliased | 0.46 | 12.25 | 0.206 | 0.99 | 0.52 | 6.0 | 30 | 1.00 | 67 | 55 | – | – | 0.0 | 0 | 56.6 | 271.4 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | blurred | 1.55 | 15.53 | 0.115 | 0.57 | 0.22 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 26.3 | 66.2 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | noisy | 1.85 | 4.49 | 0.062 | 0.36 | 0.29 | 6.0 | 30 | 1.00 | 66 | 0 | – | – | 0.0 | 0 | 14.6 | 64.5 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | scanned | 2.11 | 6.85 | 0.099 | 0.53 | 0.27 | 6.0 | 30 | 1.00 | 67 | 0 | – | – | 0.0 | 0 | 20.6 | 105.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | clean | 0.11 | 2.30 | 0.049 | 0.28 | 0.28 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | aliased | 0.60 | 15.40 | 0.205 | 0.90 | 0.90 | 6.0 | 33 | 1.10 | 55 | 43 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 83 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | blurred | 1.57 | 15.87 | 0.074 | 0.39 | 0.39 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | noisy | 2.62 | 4.76 | 0.056 | 0.16 | 0.16 | 6.0 | 30 | 1.00 | 67 | 0 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| Default with Shapes | scanned | 17.00 | 21.65 | 0.112 | 0.24 | 0.23 | 6.0 | 30 | 1.00 | 67 | 17 | – | – | – | – | 3.3 | 0.3 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | clean | 0.09 | 1.88 | 0.039 | 0.29 | 0.29 | 6.0 | 30 | 1.00 | 67 | 54 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | aliased | 0.59 | 15.23 | 0.180 | 0.60 | 0.60 | 6.0 | 30 | 1.00 | 68 | 55 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | blurred | 1.56 | 15.56 | 0.118 | 0.23 | 0.23 | 6.0 | 30 | 1.00 | 67 | 55 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | noisy | 1.78 | 3.31 | 0.041 | 0.11 | 0.11 | 6.0 | 30 | 1.00 | 67 | 0 | – | – | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Shapes | scanned | 2.10 | 6.74 | 0.085 | 0.30 | 0.24 | 6.0 | 30 | 1.00 | 67 | 0 | – | – | 0.0 | 0 | 14.7 | 8.7 | 0.0 | 0.0 | – | – | – | 100 | 0.00 | – | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | clean | 0.13 | 2.72 | 0.057 | 0.31 | 0.30 | 3.0 | 28 | 2.00 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | aliased | 0.29 | 10.43 | 0.298 | 1.00 | 1.00 | 3.0 | 27 | 2.05 | 43 | 43 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 6 Colors with Auto Grouping | blurred | 0.62 | 9.31 | 0.208 | 1.34 | 1.19 | 6.0 | 100 | 5.61 | 41 | 11 | 50 | 21.5 | 0.0 | 0 | 0.8 | 0.4 | 0.0 | 0.0 | – | – | – | – | 0.00 | 23 | 0.0 | – | – | – |
|
||||
@@ -455,9 +598,9 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| 6 Colors, no Gradients | scanned | 2.16 | 2.41 | – | – | – | 6.0 | 776 | – | 20 | 12 | – | – | – | – | 9.8 | 30.4 | 0.0 | 0.0 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| 6 Colors with Gradients | clean | 0.40 | 1.09 | – | – | – | 1.3 | 39 | – | 0 | 0 | – | – | – | – | 0.5 | 1.1 | 0.0 | 0.0 | – | – | – | – | – | – | 0.8 | 83 | 0.2 | 5 |
|
||||
| 6 Colors with Gradients | aliased | 0.22 | 0.77 | – | – | – | 0.5 | 6 | – | 0 | 0 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | – | – | 1.0 | 100 | 0.2 | 5 |
|
||||
| 6 Colors with Gradients | blurred | 0.33 | 2.29 | – | – | – | 3.0 | 161 | – | 8 | 0 | – | – | – | – | 2.3 | 5.1 | 0.0 | 0.0 | – | – | – | – | – | – | 1.0 | 100 | 0.3 | 6 |
|
||||
| 6 Colors with Gradients | noisy | 1.01 | 1.36 | – | – | – | 11.0 | 321 | – | 9 | 1 | – | – | – | – | 5.6 | 8.0 | 0.0 | 0.0 | – | – | – | – | – | – | 2.5 | 100 | 0.2 | 8 |
|
||||
| 6 Colors with Gradients | scanned | 1.57 | 1.82 | – | – | – | 5.5 | 279 | – | 23 | 14 | – | – | – | – | 3.2 | 2.4 | 0.0 | 0.0 | – | – | – | – | – | – | 0.5 | 50 | 1.5 | 21 |
|
||||
| 6 Colors with Gradients | blurred | 0.33 | 2.23 | – | – | – | 0.5 | 24 | – | 0 | 0 | – | – | – | – | 7.1 | 3.7 | 0.0 | 0.0 | – | – | – | – | – | – | 1.0 | 100 | 0.3 | 6 |
|
||||
| 6 Colors with Gradients | noisy | 1.02 | 1.27 | – | – | – | 0.5 | 14 | – | 0 | 0 | – | – | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | – | – | 1.0 | 100 | 0.2 | 8 |
|
||||
| 6 Colors with Gradients | scanned | 1.57 | 1.82 | – | – | – | 3.0 | 264 | – | 3 | 0 | – | – | – | – | 2.8 | 1.6 | 0.0 | 0.0 | – | – | – | – | – | – | 0.5 | 50 | 1.5 | 21 |
|
||||
| High Fidelity Photo, no Gradients | clean | 0.64 | 3.29 | – | – | – | 34.5 | 4318 | – | 32 | 31 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| High Fidelity Photo, no Gradients | aliased | 0.67 | 3.46 | – | – | – | 37.5 | 3184 | – | 32 | 31 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| High Fidelity Photo, no Gradients | blurred | 0.72 | 3.25 | – | – | – | 33.5 | 3324 | – | 32 | 31 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
@@ -465,9 +608,79 @@ All sizes and variants (clean, aliased, blurred, noisy, scanned) of those inputs
|
||||
| High Fidelity Photo, no Gradients | scanned | 1.05 | 1.62 | – | – | – | 54.5 | 3858 | – | 29 | 28 | – | – | – | – | 0.0 | – | 7.5 | 0.0 | – | – | – | – | – | – | 0.0 | 0 | – | – |
|
||||
| High Fidelity Photo with Gradients | clean | 0.23 | 1.19 | – | – | – | 6.0 | 252 | – | 0 | 0 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | – | – | 0.8 | 83 | 0.2 | 5 |
|
||||
| High Fidelity Photo with Gradients | aliased | 0.21 | 0.52 | – | – | – | 0.5 | 24 | – | 0 | 0 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | – | – | 1.0 | 100 | 0.2 | 5 |
|
||||
| High Fidelity Photo with Gradients | blurred | 0.33 | 2.29 | – | – | – | 55.5 | 940 | – | 4 | 0 | – | – | – | – | 0.0 | – | 13.5 | 0.0 | – | – | – | – | – | – | 1.0 | 100 | 0.3 | 6 |
|
||||
| High Fidelity Photo with Gradients | noisy | 1.00 | 1.24 | – | – | – | 23.5 | 652 | – | 21 | 7 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | – | – | 2.0 | 100 | 0.2 | 8 |
|
||||
| High Fidelity Photo with Gradients | scanned | 0.97 | 1.55 | – | – | – | 46.5 | 2942 | – | 18 | 15 | – | – | – | – | 0.0 | – | 6.0 | 0.0 | – | – | – | – | – | – | 0.5 | 50 | 1.5 | 21 |
|
||||
| High Fidelity Photo with Gradients | blurred | 0.33 | 2.20 | – | – | – | 0.5 | 48 | – | 1 | 0 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | – | – | 1.0 | 100 | 0.3 | 6 |
|
||||
| High Fidelity Photo with Gradients | noisy | 1.02 | 1.21 | – | – | – | 1.5 | 116 | – | 0 | 0 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | – | – | 1.0 | 100 | 0.2 | 8 |
|
||||
| High Fidelity Photo with Gradients | scanned | 0.97 | 1.55 | – | – | – | 37.0 | 2868 | – | 5 | 3 | – | – | – | – | 0.0 | – | 5.5 | 0.0 | – | – | – | – | – | – | 0.5 | 50 | 1.5 | 21 |
|
||||
| Default, enhanced | clean | 0.08 | 2.94 | 0.051 | 0.50 | 1.26 | 1.9 | 13 | 1.32 | 50 | 10 | 100 | 0.0 | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 14 | 0.0 | – | – | – |
|
||||
| Default, enhanced | aliased | 0.36 | 19.23 | 0.194 | 1.00 | 1.35 | 1.9 | 14 | 1.42 | 28 | 8 | 98 | 0.0 | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.14 | 14 | 0.0 | – | – | – |
|
||||
| Default, enhanced | blurred | 0.81 | 16.50 | 0.073 | 0.98 | 2.18 | 1.9 | 22 | 2.15 | 17 | 5 | 38 | 1.3 | – | – | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 14 | 0.0 | – | – | – |
|
||||
| Default, enhanced | noisy | 1.74 | 4.45 | 0.053 | 0.46 | 1.25 | 1.9 | 13 | 1.36 | 44 | 0 | 100 | 0.0 | – | – | 1.2 | 0.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | 14 | 0.0 | – | – | – |
|
||||
| Default, enhanced | scanned | 15.06 | 20.14 | 0.111 | 0.74 | 2.14 | 1.9 | 22 | 2.23 | 13 | 0 | 43 | 1.3 | – | – | 1.0 | 3.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 14 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | clean | 0.13 | 2.72 | 0.057 | 0.31 | 0.30 | 3.0 | 28 | 2.00 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | aliased | 0.29 | 10.42 | 0.293 | 1.00 | 1.00 | 3.0 | 29 | 2.15 | 43 | 43 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | blurred | 0.62 | 9.36 | 0.208 | 1.33 | 0.72 | 6.0 | 112 | 6.21 | 46 | 16 | 100 | 21.0 | 0.0 | 0 | 0.2 | 0.4 | 0.0 | 0.0 | – | – | – | – | 0.00 | 23 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | noisy | 0.85 | 2.62 | 0.073 | 0.32 | 0.32 | 3.0 | 27 | 1.99 | 35 | 0 | 100 | 0.0 | 0.0 | 0 | 0.3 | 0.5 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| Auto Color, enhanced | scanned | 1.73 | 4.43 | 0.229 | 1.20 | 0.68 | 7.0 | 84 | 4.87 | 39 | 0 | 100 | 21.0 | 0.0 | 0 | 8.9 | 6.9 | 0.0 | 0.0 | – | – | – | – | 0.00 | 15 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | clean | 1.80 | 2.05 | – | – | – | 134.7 | 11212 | – | 39 | 37 | – | – | – | – | 0.0 | – | 7.3 | 0.0 | – | – | – | – | – | 69 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | aliased | 1.78 | 2.01 | – | – | – | 73.0 | 8021 | – | 40 | 38 | – | – | – | – | 0.0 | – | 5.5 | 0.0 | – | – | – | – | – | 65 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | blurred | 1.59 | 1.95 | – | – | – | 72.0 | 7450 | – | 40 | 39 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | – | 65 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | noisy | 2.02 | 2.19 | – | – | – | 155.5 | 10670 | – | 33 | 31 | – | – | – | – | 0.0 | – | 18.5 | 0.0 | – | – | – | – | – | 72 | 0.0 | – | – | – |
|
||||
| High Fidelity Photo, enhanced | scanned | 1.45 | 1.55 | – | – | – | 140.5 | 9723 | – | 36 | 35 | – | – | – | – | 0.0 | – | 24.5 | 0.0 | – | – | – | – | – | 75 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | clean | 2.53 | 2.80 | – | – | – | 42.7 | 2307 | – | 43 | 37 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 60 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | aliased | 2.53 | 2.78 | – | – | – | 36.5 | 1505 | – | 46 | 38 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 68 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | blurred | 2.35 | 2.75 | – | – | – | 29.0 | 1990 | – | 44 | 37 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 74 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | noisy | 2.70 | 2.86 | – | – | – | 32.0 | 1972 | – | 43 | 38 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 57 | 0.0 | – | – | – |
|
||||
| Low Fidelity Photo, enhanced | scanned | 1.95 | 2.05 | – | – | – | 34.5 | 2550 | – | 42 | 33 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 61 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | clean | 0.06 | 2.00 | 0.070 | 0.31 | 0.30 | 2.0 | 11 | 1.78 | 39 | 0 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | aliased | 0.13 | 8.77 | 0.242 | 0.91 | 0.91 | 2.0 | 12 | 2.00 | 39 | 39 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | blurred | 0.34 | 7.83 | 0.073 | 0.24 | 0.24 | 2.0 | 9 | 1.50 | 39 | 0 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | noisy | 0.71 | 2.03 | 0.114 | 0.32 | 0.32 | 2.0 | 11 | 1.83 | 39 | 0 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 3 Colors, enhanced | scanned | 1.57 | 3.22 | 0.089 | 0.30 | 0.30 | 2.0 | 12 | 2.00 | 39 | 0 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | clean | 0.13 | 2.72 | 0.057 | 0.31 | 0.30 | 3.0 | 28 | 2.00 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | aliased | 0.29 | 10.42 | 0.293 | 1.00 | 1.00 | 3.0 | 29 | 2.15 | 43 | 43 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | blurred | 0.62 | 9.33 | 0.205 | 1.34 | 0.68 | 6.0 | 94 | 5.31 | 46 | 16 | 100 | 21.5 | 0.0 | 0 | 0.8 | 0.4 | 0.0 | 0.0 | – | – | – | – | 0.00 | 23 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | noisy | 0.85 | 2.59 | 0.073 | 0.32 | 0.32 | 3.0 | 27 | 1.99 | 35 | 0 | 100 | 0.0 | 0.0 | 0 | 0.3 | 0.3 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 6 Colors, enhanced | scanned | 1.74 | 4.42 | 0.228 | 1.20 | 0.68 | 7.0 | 85 | 4.95 | 39 | 0 | 100 | 21.0 | 0.0 | 0 | 0.0 | 1.1 | 0.0 | 0.0 | – | – | – | – | 0.00 | 15 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | clean | 0.12 | 2.53 | 0.052 | 0.31 | 0.30 | 3.0 | 28 | 2.11 | 43 | 19 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | aliased | 0.29 | 10.38 | 0.289 | 1.00 | 1.00 | 3.0 | 30 | 2.20 | 43 | 43 | 100 | 0.0 | 0.0 | 0 | 0.0 | 0.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | blurred | 0.62 | 9.37 | 0.213 | 1.31 | 0.74 | 7.0 | 133 | 7.23 | 48 | 17 | 100 | 25.5 | 0.0 | 0 | 0.2 | 0.5 | 0.0 | 0.0 | – | – | – | – | 0.00 | 27 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | noisy | 0.85 | 2.57 | 0.072 | 0.32 | 0.32 | 3.0 | 28 | 2.10 | 34 | 0 | 100 | 0.0 | 0.0 | 0 | 0.2 | 0.2 | 0.0 | 0.0 | – | – | – | – | 0.00 | 30 | 0.0 | – | – | – |
|
||||
| 16 Colors, enhanced | scanned | 1.72 | 4.44 | 0.257 | 1.09 | 0.80 | 7.5 | 140 | 7.91 | 41 | 0 | 100 | 28.5 | 0.0 | 0 | 2.6 | 3.0 | 0.0 | 0.0 | – | – | – | – | 0.00 | 18 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | clean | 9.71 | 9.55 | – | – | – | 18.3 | 753 | – | 51 | 41 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | 0.00 | 53 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | aliased | 9.71 | 9.52 | – | – | – | 11.0 | 647 | – | 57 | 41 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | 0.00 | 54 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | blurred | 9.66 | 9.32 | – | – | – | 11.5 | 585 | – | 55 | 41 | – | – | – | – | 0.0 | – | 0.5 | 0.0 | – | – | – | – | 0.00 | 47 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | noisy | 9.74 | 9.44 | – | – | – | 19.5 | 648 | – | 46 | 37 | – | – | – | – | 0.0 | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 28 | 0.0 | – | – | – |
|
||||
| Shades of Gray, enhanced | scanned | 6.58 | 6.46 | – | – | – | 19.0 | 764 | – | 47 | 37 | – | – | – | – | 0.0 | – | 1.0 | 0.0 | – | – | – | – | 0.00 | 37 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | clean | 0.05 | 1.98 | 0.032 | 0.50 | 1.26 | 1.4 | 14 | 1.39 | 55 | 15 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | aliased | 0.38 | 20.07 | 0.166 | 1.00 | 1.35 | 1.4 | 20 | 2.13 | 54 | 14 | 98 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.14 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | blurred | 0.80 | 16.27 | 0.056 | 0.83 | 2.17 | 1.4 | 31 | 3.07 | 30 | 12 | 38 | 1.1 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | noisy | 1.73 | 4.16 | 0.038 | 0.46 | 1.25 | 1.4 | 14 | 1.41 | 47 | 7 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Black and White Logo, enhanced | scanned | 15.05 | 19.98 | 0.097 | 0.76 | 2.07 | 1.4 | 43 | 4.19 | 24 | 1 | 43 | 1.1 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | clean | 0.20 | 5.45 | 0.105 | 0.96 | 52.59 | 1.9 | 15 | 1.28 | 26 | 14 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.07 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | aliased | 0.45 | 19.03 | 0.194 | 1.00 | 105.54 | 1.6 | 11 | 1.06 | 26 | 9 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | blurred | 1.03 | 18.97 | 0.226 | 0.81 | 105.58 | 1.6 | 16 | 1.39 | 13 | 8 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | noisy | 1.75 | 5.97 | 0.108 | 0.97 | 1.06 | 2.0 | 16 | 1.34 | 17 | 0 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Sketched Art, enhanced | scanned | 15.10 | 22.30 | 0.403 | 0.78 | 105.61 | 1.6 | 20 | 1.64 | 2 | 0 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | clean | 0.35 | 14.33 | 0.277 | 0.69 | 0.69 | 1.2 | 11 | 1.24 | 16 | 16 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | aliased | 0.37 | 20.27 | 0.243 | 1.00 | 1.00 | 1.2 | 10 | 1.32 | 12 | 9 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | blurred | 1.23 | 28.51 | 0.809 | 1.41 | 1.41 | 1.2 | 12 | 1.34 | 13 | 10 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.50 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | noisy | 2.11 | 11.00 | 0.281 | 0.68 | 0.69 | 1.2 | 11 | 1.24 | 9 | 0 | 100 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Silhouettes, enhanced | scanned | 16.04 | 30.10 | 1.440 | 2.19 | 2.19 | 1.2 | 26 | 3.36 | 3 | 0 | 50 | 2.8 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.25 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | clean | 0.52 | 4.12 | 0.040 | 0.47 | 105.72 | 3.3 | 29 | 1.45 | 40 | 0 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | aliased | 0.67 | 14.44 | 0.112 | 0.73 | 55.44 | 14.0 | 59 | 2.95 | 87 | 1 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | blurred | 1.56 | 16.08 | 0.057 | 0.99 | 105.72 | 3.0 | 49 | 2.45 | 38 | 0 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | noisy | 1.03 | 4.10 | 0.035 | 0.44 | 56.07 | 8.0 | 50 | 2.50 | 43 | 0 | – | – | – | – | – | – | 1.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Line Art, enhanced | scanned | 13.54 | 19.55 | 0.062 | 0.92 | 105.68 | 3.0 | 51 | 2.55 | 8 | 0 | – | – | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | clean | 0.24 | 4.91 | 0.095 | 1.46 | 55.93 | 4.9 | 24 | 1.45 | 97 | 21 | 100 | 0.0 | – | – | – | – | 0.7 | 0.0 | – | – | – | – | 0.13 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | aliased | 0.50 | 19.31 | 0.229 | 1.37 | 55.44 | 6.2 | 31 | 1.79 | 98 | 23 | 97 | 0.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.20 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | blurred | 1.06 | 16.55 | 0.138 | 1.16 | 105.64 | 2.4 | 41 | 3.21 | 60 | 13 | 51 | 2.7 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | noisy | 1.59 | 5.75 | 0.103 | 1.17 | 31.79 | 7.0 | 32 | 1.86 | 97 | 21 | 100 | 0.0 | – | – | – | – | 1.4 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Technical Drawing, enhanced | scanned | 14.69 | 20.14 | 0.138 | 1.17 | 105.57 | 2.4 | 63 | 4.62 | 59 | 12 | 57 | 3.0 | – | – | – | – | 0.0 | 0.0 | – | – | – | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | clean | 0.51 | 3.57 | 0.028 | 0.41 | 105.74 | 3.3 | 7 | 0.33 | 100 | 7 | – | – | – | – | – | – | 0.0 | 0.0 | 0.020 | 0.02 | 2.0 | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | aliased | 0.74 | 16.02 | 0.164 | 0.81 | 55.55 | 11.0 | 25 | 1.25 | 92 | 9 | – | – | – | – | – | – | 0.0 | 0.0 | 0.158 | 0.01 | 2.3 | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | blurred | 1.57 | 16.26 | 0.032 | 0.86 | 105.74 | 3.0 | 6 | 0.30 | 100 | 0 | – | – | – | – | – | – | 0.0 | 0.0 | 0.013 | 0.03 | 2.0 | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | noisy | 0.94 | 3.36 | 0.024 | 0.41 | 55.94 | 8.0 | 16 | 0.80 | 100 | 2 | – | – | – | – | – | – | 0.0 | 0.0 | 0.011 | 0.02 | 2.0 | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
| Outline, enhanced | scanned | 13.53 | 19.37 | 0.045 | 0.88 | 105.64 | 3.0 | 6 | 0.30 | 100 | 0 | – | – | – | – | – | – | 0.0 | 0.0 | 0.027 | 0.03 | 2.0 | – | 0.00 | 0 | 0.0 | – | – | – |
|
||||
|
||||
## 4. Yardstick
|
||||
|
||||
@@ -509,155 +722,241 @@ One trace at a time on this machine (all cores); memory is the peak allocated ab
|
||||
|
||||
| preset | image | megapixels | result |
|
||||
|:--|:--|--:|:--|
|
||||
| Default | photo-like | 1 | 0.05 s, 29 paths, 6278 anchors, 12 MB |
|
||||
| Default | four-colour badge | 1 | 0.03 s, 3 paths, 64 anchors, 12 MB |
|
||||
| Auto Color | photo-like | 1 | 0.18 s, 732 paths, 45265 anchors, 23 MB |
|
||||
| Auto Color | four-colour badge | 1 | 0.06 s, 5 paths, 84 anchors, 13 MB |
|
||||
| High Fidelity Photo | photo-like | 1 | 0.26 s, 968 paths, 117041 anchors, 145 MB |
|
||||
| Default | photo-like | 1 | 0.04 s, 29 paths, 6278 anchors, 12 MB |
|
||||
| Default | four-colour badge | 1 | 0.02 s, 3 paths, 64 anchors, 12 MB |
|
||||
| Auto Color | photo-like | 1 | 0.17 s, 732 paths, 45265 anchors, 24 MB |
|
||||
| Auto Color | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| High Fidelity Photo | photo-like | 1 | 0.22 s, 968 paths, 117041 anchors, 171 MB |
|
||||
| High Fidelity Photo | four-colour badge | 1 | 0.06 s, 6 paths, 172 anchors, 23 MB |
|
||||
| Low Fidelity Photo | photo-like | 1 | 0.15 s, 129 paths, 23014 anchors, 104 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 1 | 0.07 s, 6 paths, 136 anchors, 23 MB |
|
||||
| 3 Colors | photo-like | 1 | 0.11 s, 9 paths, 3341 anchors, 18 MB |
|
||||
| 3 Colors | four-colour badge | 1 | 0.06 s, 4 paths, 88 anchors, 12 MB |
|
||||
| Low Fidelity Photo | photo-like | 1 | 0.12 s, 129 paths, 23014 anchors, 105 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 1 | 0.05 s, 6 paths, 136 anchors, 23 MB |
|
||||
| 3 Colors | photo-like | 1 | 0.09 s, 9 paths, 3341 anchors, 18 MB |
|
||||
| 3 Colors | four-colour badge | 1 | 0.05 s, 4 paths, 88 anchors, 13 MB |
|
||||
| 6 Colors | photo-like | 1 | 0.10 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 16 Colors | photo-like | 1 | 0.13 s, 178 paths, 20470 anchors, 31 MB |
|
||||
| 16 Colors | four-colour badge | 1 | 0.06 s, 5 paths, 88 anchors, 12 MB |
|
||||
| Shades of Gray | photo-like | 1 | 0.11 s, 81 paths, 6698 anchors, 40 MB |
|
||||
| Shades of Gray | four-colour badge | 1 | 0.07 s, 10 paths, 725 anchors, 35 MB |
|
||||
| Black and White Logo | photo-like | 1 | 0.05 s, 71 paths, 4933 anchors, 8 MB |
|
||||
| Black and White Logo | four-colour badge | 1 | 0.03 s, 1 paths, 47 anchors, 6 MB |
|
||||
| Sketched Art | photo-like | 1 | 0.05 s, 5 paths, 2873 anchors, 8 MB |
|
||||
| Sketched Art | four-colour badge | 1 | 0.03 s, 2 paths, 114 anchors, 6 MB |
|
||||
| Silhouettes | photo-like | 1 | 0.04 s, 2 paths, 556 anchors, 9 MB |
|
||||
| 16 Colors | photo-like | 1 | 0.11 s, 178 paths, 20470 anchors, 26 MB |
|
||||
| 16 Colors | four-colour badge | 1 | 0.05 s, 5 paths, 88 anchors, 12 MB |
|
||||
| Shades of Gray | photo-like | 1 | 0.10 s, 81 paths, 6698 anchors, 41 MB |
|
||||
| Shades of Gray | four-colour badge | 1 | 0.06 s, 10 paths, 725 anchors, 34 MB |
|
||||
| Black and White Logo | photo-like | 1 | 0.04 s, 71 paths, 4933 anchors, 8 MB |
|
||||
| Black and White Logo | four-colour badge | 1 | 0.02 s, 1 paths, 47 anchors, 6 MB |
|
||||
| Sketched Art | photo-like | 1 | 0.04 s, 5 paths, 2873 anchors, 8 MB |
|
||||
| Sketched Art | four-colour badge | 1 | 0.02 s, 2 paths, 114 anchors, 6 MB |
|
||||
| Silhouettes | photo-like | 1 | 0.03 s, 2 paths, 556 anchors, 9 MB |
|
||||
| Silhouettes | four-colour badge | 1 | 0.02 s, 1 paths, 24 anchors, 6 MB |
|
||||
| Line Art | photo-like | 1 | 0.05 s, 54 paths, 4495 anchors, 8 MB |
|
||||
| Line Art | photo-like | 1 | 0.04 s, 54 paths, 4495 anchors, 8 MB |
|
||||
| Line Art | four-colour badge | 1 | 0.02 s, 1 paths, 38 anchors, 6 MB |
|
||||
| Technical Drawing | photo-like | 1 | 0.05 s, 221 paths, 6258 anchors, 8 MB |
|
||||
| Technical Drawing | four-colour badge | 1 | 0.03 s, 1 paths, 47 anchors, 6 MB |
|
||||
| Outline | photo-like | 1 | 0.07 s, 45 paths, 4110 anchors, 16 MB |
|
||||
| Technical Drawing | photo-like | 1 | 0.04 s, 221 paths, 6258 anchors, 8 MB |
|
||||
| Technical Drawing | four-colour badge | 1 | 0.02 s, 1 paths, 47 anchors, 6 MB |
|
||||
| Outline | photo-like | 1 | 0.05 s, 45 paths, 4110 anchors, 16 MB |
|
||||
| Outline | four-colour badge | 1 | 0.03 s, 1 paths, 38 anchors, 11 MB |
|
||||
| Flat Logo | photo-like | 1 | refused after 0.15 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 1 | 0.85 s, 5 paths, 78 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 1 | 0.07 s, 29 paths, 6278 anchors, 12 MB |
|
||||
| Default with Shapes | four-colour badge | 1 | 0.03 s, 3 paths, 63 anchors, 12 MB |
|
||||
| Flat Logo | photo-like | 1 | refused after 0.14 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 1 | 0.66 s, 5 paths, 78 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 1 | 0.05 s, 29 paths, 6278 anchors, 12 MB |
|
||||
| Default with Shapes | four-colour badge | 1 | 0.02 s, 3 paths, 62 anchors, 12 MB |
|
||||
| 6 Colors with Shapes | photo-like | 1 | 0.11 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 1 | 0.06 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 1 | 0.11 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 1 | 0.06 s, 5 paths, 84 anchors, 12 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 1 | 0.12 s, 81 paths, 6698 anchors, 44 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 1 | 0.08 s, 10 paths, 725 anchors, 34 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 1 | 0.29 s, 968 paths, 117041 anchors, 135 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 1 | 0.06 s, 6 paths, 172 anchors, 24 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 1 | 0.11 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 1 | 0.06 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 1 | 0.10 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 1 | 0.10 s, 81 paths, 6698 anchors, 41 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 1 | 0.06 s, 10 paths, 725 anchors, 34 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 1 | 0.24 s, 968 paths, 117041 anchors, 169 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 1 | 0.05 s, 6 paths, 172 anchors, 23 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 1 | 0.09 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 1 | 0.07 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 6 Colors with Gradients | photo-like | 1 | 0.15 s, 78 paths, 9076 anchors, 32 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 1 | 0.10 s, 5 paths, 84 anchors, 12 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 1 | 0.26 s, 968 paths, 117041 anchors, 146 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 1 | 0.07 s, 6 paths, 172 anchors, 23 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 1 | 0.31 s, 968 paths, 117041 anchors, 155 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 1 | 0.10 s, 6 paths, 172 anchors, 23 MB |
|
||||
| Default | photo-like | 4 | 0.23 s, 48 paths, 14438 anchors, 49 MB |
|
||||
| Default | four-colour badge | 4 | 0.11 s, 3 paths, 72 anchors, 48 MB |
|
||||
| Auto Color | photo-like | 4 | 0.53 s, 1469 paths, 149389 anchors, 110 MB |
|
||||
| Auto Color | four-colour badge | 4 | 0.23 s, 5 paths, 92 anchors, 46 MB |
|
||||
| High Fidelity Photo | photo-like | 4 | 0.81 s, 4479 paths, 337884 anchors, 630 MB |
|
||||
| High Fidelity Photo | four-colour badge | 4 | 0.27 s, 6 paths, 200 anchors, 97 MB |
|
||||
| Low Fidelity Photo | photo-like | 4 | 0.43 s, 299 paths, 61601 anchors, 396 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 4 | 0.26 s, 6 paths, 152 anchors, 94 MB |
|
||||
| 3 Colors | photo-like | 4 | 0.34 s, 20 paths, 8473 anchors, 73 MB |
|
||||
| 3 Colors | four-colour badge | 4 | 0.22 s, 4 paths, 122 anchors, 53 MB |
|
||||
| 6 Colors | photo-like | 4 | 0.35 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 1 | 0.08 s, 5 paths, 84 anchors, 12 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 1 | 0.23 s, 968 paths, 117041 anchors, 170 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 1 | 0.06 s, 6 paths, 172 anchors, 23 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 1 | 0.26 s, 968 paths, 117041 anchors, 169 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 1 | 0.09 s, 6 paths, 172 anchors, 23 MB |
|
||||
| Default, enhanced | photo-like | 1 | 0.05 s, 29 paths, 6278 anchors, 12 MB |
|
||||
| Default, enhanced | four-colour badge | 1 | 0.02 s, 3 paths, 62 anchors, 12 MB |
|
||||
| Auto Color, enhanced | photo-like | 1 | 0.17 s, 732 paths, 45260 anchors, 27 MB |
|
||||
| Auto Color, enhanced | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| High Fidelity Photo, enhanced | photo-like | 1 | 0.26 s, 968 paths, 117041 anchors, 169 MB |
|
||||
| High Fidelity Photo, enhanced | four-colour badge | 1 | 0.05 s, 6 paths, 172 anchors, 23 MB |
|
||||
| Low Fidelity Photo, enhanced | photo-like | 1 | 0.14 s, 129 paths, 23014 anchors, 105 MB |
|
||||
| Low Fidelity Photo, enhanced | four-colour badge | 1 | 0.05 s, 6 paths, 116 anchors, 23 MB |
|
||||
| 3 Colors, enhanced | photo-like | 1 | 0.09 s, 9 paths, 3341 anchors, 18 MB |
|
||||
| 3 Colors, enhanced | four-colour badge | 1 | 0.05 s, 4 paths, 76 anchors, 13 MB |
|
||||
| 6 Colors, enhanced | photo-like | 1 | 0.10 s, 78 paths, 9076 anchors, 24 MB |
|
||||
| 6 Colors, enhanced | four-colour badge | 1 | 0.05 s, 5 paths, 84 anchors, 12 MB |
|
||||
| 16 Colors, enhanced | photo-like | 1 | 0.13 s, 178 paths, 20467 anchors, 31 MB |
|
||||
| 16 Colors, enhanced | four-colour badge | 1 | 0.05 s, 5 paths, 88 anchors, 12 MB |
|
||||
| Shades of Gray, enhanced | photo-like | 1 | 0.10 s, 81 paths, 6698 anchors, 40 MB |
|
||||
| Shades of Gray, enhanced | four-colour badge | 1 | 0.06 s, 10 paths, 623 anchors, 35 MB |
|
||||
| Black and White Logo, enhanced | photo-like | 1 | 0.05 s, 71 paths, 4933 anchors, 8 MB |
|
||||
| Black and White Logo, enhanced | four-colour badge | 1 | 0.02 s, 1 paths, 46 anchors, 6 MB |
|
||||
| Sketched Art, enhanced | photo-like | 1 | 0.04 s, 5 paths, 2873 anchors, 8 MB |
|
||||
| Sketched Art, enhanced | four-colour badge | 1 | 0.02 s, 2 paths, 37 anchors, 6 MB |
|
||||
| Silhouettes, enhanced | photo-like | 1 | 0.04 s, 2 paths, 556 anchors, 9 MB |
|
||||
| Silhouettes, enhanced | four-colour badge | 1 | 0.02 s, 1 paths, 8 anchors, 6 MB |
|
||||
| Line Art, enhanced | photo-like | 1 | 0.05 s, 54 paths, 4495 anchors, 8 MB |
|
||||
| Line Art, enhanced | four-colour badge | 1 | 0.02 s, 1 paths, 37 anchors, 6 MB |
|
||||
| Technical Drawing, enhanced | photo-like | 1 | 0.05 s, 221 paths, 6258 anchors, 8 MB |
|
||||
| Technical Drawing, enhanced | four-colour badge | 1 | 0.02 s, 1 paths, 46 anchors, 6 MB |
|
||||
| Outline, enhanced | photo-like | 1 | 0.05 s, 45 paths, 4110 anchors, 16 MB |
|
||||
| Outline, enhanced | four-colour badge | 1 | 0.03 s, 1 paths, 38 anchors, 11 MB |
|
||||
| Default | photo-like | 4 | 0.20 s, 48 paths, 14438 anchors, 49 MB |
|
||||
| Default | four-colour badge | 4 | 0.10 s, 3 paths, 72 anchors, 48 MB |
|
||||
| Auto Color | photo-like | 4 | 0.44 s, 1469 paths, 149389 anchors, 105 MB |
|
||||
| Auto Color | four-colour badge | 4 | 0.19 s, 5 paths, 92 anchors, 47 MB |
|
||||
| High Fidelity Photo | photo-like | 4 | 0.80 s, 4479 paths, 337884 anchors, 636 MB |
|
||||
| High Fidelity Photo | four-colour badge | 4 | 0.25 s, 6 paths, 200 anchors, 94 MB |
|
||||
| Low Fidelity Photo | photo-like | 4 | 0.40 s, 299 paths, 61601 anchors, 395 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 4 | 0.41 s, 6 paths, 152 anchors, 97 MB |
|
||||
| 3 Colors | photo-like | 4 | 0.79 s, 20 paths, 8473 anchors, 73 MB |
|
||||
| 3 Colors | four-colour badge | 4 | 0.30 s, 4 paths, 122 anchors, 53 MB |
|
||||
| 6 Colors | photo-like | 4 | 0.34 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors | four-colour badge | 4 | 0.22 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 16 Colors | photo-like | 4 | 0.35 s, 353 paths, 52770 anchors, 130 MB |
|
||||
| 16 Colors | four-colour badge | 4 | 0.22 s, 5 paths, 96 anchors, 46 MB |
|
||||
| Shades of Gray | photo-like | 4 | 0.35 s, 137 paths, 18823 anchors, 168 MB |
|
||||
| Shades of Gray | four-colour badge | 4 | 0.30 s, 10 paths, 1256 anchors, 137 MB |
|
||||
| Black and White Logo | photo-like | 4 | 0.25 s, 208 paths, 13546 anchors, 32 MB |
|
||||
| Black and White Logo | four-colour badge | 4 | 0.10 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Sketched Art | photo-like | 4 | 0.22 s, 9 paths, 5387 anchors, 32 MB |
|
||||
| Sketched Art | four-colour badge | 4 | 0.10 s, 2 paths, 196 anchors, 24 MB |
|
||||
| Silhouettes | photo-like | 4 | 0.22 s, 2 paths, 1465 anchors, 36 MB |
|
||||
| Silhouettes | four-colour badge | 4 | 0.10 s, 1 paths, 28 anchors, 24 MB |
|
||||
| Line Art | photo-like | 4 | 0.23 s, 127 paths, 11330 anchors, 32 MB |
|
||||
| 16 Colors | photo-like | 4 | 0.34 s, 353 paths, 52770 anchors, 129 MB |
|
||||
| 16 Colors | four-colour badge | 4 | 0.22 s, 5 paths, 96 anchors, 47 MB |
|
||||
| Shades of Gray | photo-like | 4 | 0.32 s, 137 paths, 18823 anchors, 156 MB |
|
||||
| Shades of Gray | four-colour badge | 4 | 0.28 s, 10 paths, 1256 anchors, 136 MB |
|
||||
| Black and White Logo | photo-like | 4 | 0.20 s, 208 paths, 13546 anchors, 32 MB |
|
||||
| Black and White Logo | four-colour badge | 4 | 0.08 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Sketched Art | photo-like | 4 | 0.20 s, 9 paths, 5387 anchors, 32 MB |
|
||||
| Sketched Art | four-colour badge | 4 | 0.09 s, 2 paths, 196 anchors, 24 MB |
|
||||
| Silhouettes | photo-like | 4 | 0.21 s, 2 paths, 1465 anchors, 36 MB |
|
||||
| Silhouettes | four-colour badge | 4 | 0.08 s, 1 paths, 28 anchors, 24 MB |
|
||||
| Line Art | photo-like | 4 | 0.20 s, 127 paths, 11330 anchors, 32 MB |
|
||||
| Line Art | four-colour badge | 4 | 0.09 s, 1 paths, 42 anchors, 24 MB |
|
||||
| Technical Drawing | photo-like | 4 | 0.23 s, 931 paths, 20628 anchors, 32 MB |
|
||||
| Technical Drawing | four-colour badge | 4 | 0.10 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Outline | photo-like | 4 | 0.31 s, 113 paths, 10815 anchors, 64 MB |
|
||||
| Outline | four-colour badge | 4 | 0.15 s, 1 paths, 42 anchors, 44 MB |
|
||||
| Flat Logo | photo-like | 4 | refused after 0.19 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 4 | 0.86 s, 5 paths, 69 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 4 | 0.28 s, 48 paths, 14438 anchors, 49 MB |
|
||||
| Technical Drawing | photo-like | 4 | 0.20 s, 931 paths, 20628 anchors, 32 MB |
|
||||
| Technical Drawing | four-colour badge | 4 | 0.08 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Outline | photo-like | 4 | 0.25 s, 113 paths, 10815 anchors, 64 MB |
|
||||
| Outline | four-colour badge | 4 | 0.13 s, 1 paths, 42 anchors, 44 MB |
|
||||
| Flat Logo | photo-like | 4 | refused after 0.14 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 4 | 0.67 s, 5 paths, 69 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 4 | 0.22 s, 48 paths, 14438 anchors, 49 MB |
|
||||
| Default with Shapes | four-colour badge | 4 | 0.10 s, 3 paths, 72 anchors, 48 MB |
|
||||
| 6 Colors with Shapes | photo-like | 4 | 0.34 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 4 | 0.24 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 4 | 0.37 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 4 | 0.22 s, 5 paths, 92 anchors, 46 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 4 | 0.38 s, 137 paths, 18823 anchors, 156 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 4 | 0.34 s, 10 paths, 1256 anchors, 138 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 4 | 0.96 s, 4479 paths, 337884 anchors, 636 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 4 | 0.26 s, 6 paths, 200 anchors, 97 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 4 | 0.32 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 4 | 0.31 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 6 Colors with Gradients | photo-like | 4 | 0.65 s, 115 paths, 23295 anchors, 128 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 4 | 0.48 s, 5 paths, 92 anchors, 46 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 4 | 0.81 s, 4479 paths, 337884 anchors, 645 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 4 | 0.33 s, 6 paths, 200 anchors, 94 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 4 | 1.14 s, 4479 paths, 337884 anchors, 644 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 4 | 0.39 s, 6 paths, 200 anchors, 97 MB |
|
||||
| Default | photo-like | 16 | 1.13 s, 127 paths, 34034 anchors, 199 MB |
|
||||
| Default | four-colour badge | 16 | 0.55 s, 3 paths, 88 anchors, 184 MB |
|
||||
| Auto Color | photo-like | 16 | 2.20 s, 5390 paths, 454670 anchors, 280 MB |
|
||||
| Auto Color | four-colour badge | 16 | 1.71 s, 5 paths, 112 anchors, 190 MB |
|
||||
| High Fidelity Photo | photo-like | 16 | 5.39 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo | four-colour badge | 16 | 1.26 s, 6 paths, 264 anchors, 257 MB |
|
||||
| Low Fidelity Photo | photo-like | 16 | 2.12 s, 2036 paths, 182778 anchors, 754 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 16 | 1.22 s, 6 paths, 180 anchors, 257 MB |
|
||||
| 3 Colors | photo-like | 16 | 1.41 s, 76 paths, 23024 anchors, 290 MB |
|
||||
| 3 Colors | four-colour badge | 16 | 0.83 s, 4 paths, 190 anchors, 205 MB |
|
||||
| 6 Colors | photo-like | 16 | 1.24 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors | four-colour badge | 16 | 0.87 s, 5 paths, 112 anchors, 184 MB |
|
||||
| 16 Colors | photo-like | 16 | 1.14 s, 1973 paths, 145642 anchors, 298 MB |
|
||||
| 16 Colors | four-colour badge | 16 | 0.86 s, 5 paths, 114 anchors, 184 MB |
|
||||
| Shades of Gray | photo-like | 16 | 1.66 s, 686 paths, 53654 anchors, 407 MB |
|
||||
| Shades of Gray | four-colour badge | 16 | 1.38 s, 9 paths, 2208 anchors, 270 MB |
|
||||
| Black and White Logo | photo-like | 16 | 1.13 s, 1128 paths, 45273 anchors, 129 MB |
|
||||
| Black and White Logo | four-colour badge | 16 | 0.39 s, 1 paths, 69 anchors, 88 MB |
|
||||
| Sketched Art | photo-like | 16 | 1.14 s, 15 paths, 11486 anchors, 128 MB |
|
||||
| Sketched Art | four-colour badge | 16 | 0.40 s, 2 paths, 361 anchors, 88 MB |
|
||||
| Silhouettes | photo-like | 16 | 1.38 s, 3 paths, 3350 anchors, 144 MB |
|
||||
| Silhouettes | four-colour badge | 16 | 0.43 s, 1 paths, 32 anchors, 95 MB |
|
||||
| Line Art | photo-like | 16 | 1.13 s, 713 paths, 37370 anchors, 129 MB |
|
||||
| Line Art | four-colour badge | 16 | 0.40 s, 1 paths, 49 anchors, 88 MB |
|
||||
| Technical Drawing | photo-like | 16 | 1.27 s, 4577 paths, 76559 anchors, 130 MB |
|
||||
| Technical Drawing | four-colour badge | 16 | 0.39 s, 1 paths, 69 anchors, 88 MB |
|
||||
| Outline | photo-like | 16 | 1.32 s, 632 paths, 34884 anchors, 256 MB |
|
||||
| Outline | four-colour badge | 16 | 0.59 s, 1 paths, 49 anchors, 176 MB |
|
||||
| Flat Logo | photo-like | 16 | refused after 0.16 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 16 | 0.68 s, 5 paths, 67 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 16 | 1.20 s, 127 paths, 34034 anchors, 199 MB |
|
||||
| 6 Colors with Shapes | photo-like | 4 | 0.31 s, 115 paths, 23292 anchors, 100 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 4 | 0.19 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 4 | 0.34 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 4 | 0.21 s, 5 paths, 92 anchors, 47 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 4 | 0.32 s, 137 paths, 18823 anchors, 155 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 4 | 0.30 s, 10 paths, 1256 anchors, 136 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 4 | 0.85 s, 4479 paths, 337884 anchors, 639 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 4 | 0.23 s, 6 paths, 200 anchors, 94 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 4 | 0.27 s, 115 paths, 23295 anchors, 100 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 4 | 0.19 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 6 Colors with Gradients | photo-like | 4 | 0.78 s, 115 paths, 23295 anchors, 128 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 4 | 0.49 s, 5 paths, 92 anchors, 48 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 4 | 0.72 s, 4479 paths, 337884 anchors, 633 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 4 | 0.23 s, 6 paths, 200 anchors, 97 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 4 | 1.06 s, 4479 paths, 337884 anchors, 642 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 4 | 0.34 s, 6 paths, 200 anchors, 96 MB |
|
||||
| Default, enhanced | photo-like | 4 | 0.22 s, 48 paths, 14438 anchors, 49 MB |
|
||||
| Default, enhanced | four-colour badge | 4 | 0.10 s, 3 paths, 72 anchors, 48 MB |
|
||||
| Auto Color, enhanced | photo-like | 4 | 0.49 s, 1469 paths, 149374 anchors, 113 MB |
|
||||
| Auto Color, enhanced | four-colour badge | 4 | 0.18 s, 5 paths, 92 anchors, 46 MB |
|
||||
| High Fidelity Photo, enhanced | photo-like | 4 | 0.83 s, 4479 paths, 337884 anchors, 638 MB |
|
||||
| High Fidelity Photo, enhanced | four-colour badge | 4 | 0.23 s, 6 paths, 200 anchors, 93 MB |
|
||||
| Low Fidelity Photo, enhanced | photo-like | 4 | 0.39 s, 299 paths, 61598 anchors, 408 MB |
|
||||
| Low Fidelity Photo, enhanced | four-colour badge | 4 | 0.23 s, 6 paths, 128 anchors, 93 MB |
|
||||
| 3 Colors, enhanced | photo-like | 4 | 0.28 s, 20 paths, 8471 anchors, 73 MB |
|
||||
| 3 Colors, enhanced | four-colour badge | 4 | 0.19 s, 4 paths, 84 anchors, 51 MB |
|
||||
| 6 Colors, enhanced | photo-like | 4 | 0.30 s, 115 paths, 23292 anchors, 100 MB |
|
||||
| 6 Colors, enhanced | four-colour badge | 4 | 0.19 s, 5 paths, 92 anchors, 46 MB |
|
||||
| 16 Colors, enhanced | photo-like | 4 | 0.33 s, 353 paths, 52772 anchors, 126 MB |
|
||||
| 16 Colors, enhanced | four-colour badge | 4 | 0.19 s, 5 paths, 96 anchors, 46 MB |
|
||||
| Shades of Gray, enhanced | photo-like | 4 | 0.30 s, 137 paths, 18823 anchors, 163 MB |
|
||||
| Shades of Gray, enhanced | four-colour badge | 4 | 0.26 s, 10 paths, 1056 anchors, 137 MB |
|
||||
| Black and White Logo, enhanced | photo-like | 4 | 0.21 s, 208 paths, 13546 anchors, 32 MB |
|
||||
| Black and White Logo, enhanced | four-colour badge | 4 | 0.08 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Sketched Art, enhanced | photo-like | 4 | 0.19 s, 9 paths, 5387 anchors, 32 MB |
|
||||
| Sketched Art, enhanced | four-colour badge | 4 | 0.08 s, 2 paths, 38 anchors, 24 MB |
|
||||
| Silhouettes, enhanced | photo-like | 4 | 0.22 s, 2 paths, 1465 anchors, 36 MB |
|
||||
| Silhouettes, enhanced | four-colour badge | 4 | 0.08 s, 1 paths, 8 anchors, 24 MB |
|
||||
| Line Art, enhanced | photo-like | 4 | 0.21 s, 127 paths, 11330 anchors, 32 MB |
|
||||
| Line Art, enhanced | four-colour badge | 4 | 0.09 s, 1 paths, 42 anchors, 24 MB |
|
||||
| Technical Drawing, enhanced | photo-like | 4 | 0.21 s, 931 paths, 20628 anchors, 32 MB |
|
||||
| Technical Drawing, enhanced | four-colour badge | 4 | 0.08 s, 1 paths, 57 anchors, 24 MB |
|
||||
| Outline, enhanced | photo-like | 4 | 0.24 s, 113 paths, 10815 anchors, 64 MB |
|
||||
| Outline, enhanced | four-colour badge | 4 | 0.12 s, 1 paths, 42 anchors, 44 MB |
|
||||
| Default | photo-like | 16 | 1.08 s, 127 paths, 34034 anchors, 199 MB |
|
||||
| Default | four-colour badge | 16 | 0.51 s, 3 paths, 88 anchors, 184 MB |
|
||||
| Auto Color | photo-like | 16 | 1.66 s, 5390 paths, 454670 anchors, 277 MB |
|
||||
| Auto Color | four-colour badge | 16 | 0.84 s, 5 paths, 112 anchors, 184 MB |
|
||||
| High Fidelity Photo | photo-like | 16 | 4.31 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo | four-colour badge | 16 | 1.10 s, 6 paths, 264 anchors, 257 MB |
|
||||
| Low Fidelity Photo | photo-like | 16 | 1.98 s, 2036 paths, 182778 anchors, 754 MB |
|
||||
| Low Fidelity Photo | four-colour badge | 16 | 1.09 s, 6 paths, 180 anchors, 257 MB |
|
||||
| 3 Colors | photo-like | 16 | 1.50 s, 76 paths, 23024 anchors, 290 MB |
|
||||
| 3 Colors | four-colour badge | 16 | 1.05 s, 4 paths, 190 anchors, 213 MB |
|
||||
| 6 Colors | photo-like | 16 | 1.57 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors | four-colour badge | 16 | 1.07 s, 5 paths, 112 anchors, 184 MB |
|
||||
| 16 Colors | photo-like | 16 | 1.68 s, 1973 paths, 145642 anchors, 303 MB |
|
||||
| 16 Colors | four-colour badge | 16 | 1.09 s, 5 paths, 114 anchors, 188 MB |
|
||||
| Shades of Gray | photo-like | 16 | 2.48 s, 686 paths, 53654 anchors, 407 MB |
|
||||
| Shades of Gray | four-colour badge | 16 | 1.96 s, 9 paths, 2208 anchors, 270 MB |
|
||||
| Black and White Logo | photo-like | 16 | 1.37 s, 1128 paths, 45273 anchors, 129 MB |
|
||||
| Black and White Logo | four-colour badge | 16 | 0.47 s, 1 paths, 69 anchors, 88 MB |
|
||||
| Sketched Art | photo-like | 16 | 1.19 s, 15 paths, 11486 anchors, 128 MB |
|
||||
| Sketched Art | four-colour badge | 16 | 0.52 s, 2 paths, 361 anchors, 88 MB |
|
||||
| Silhouettes | photo-like | 16 | 1.45 s, 3 paths, 3350 anchors, 144 MB |
|
||||
| Silhouettes | four-colour badge | 16 | 0.48 s, 1 paths, 32 anchors, 95 MB |
|
||||
| Line Art | photo-like | 16 | 1.23 s, 713 paths, 37370 anchors, 129 MB |
|
||||
| Line Art | four-colour badge | 16 | 0.50 s, 1 paths, 49 anchors, 88 MB |
|
||||
| Technical Drawing | photo-like | 16 | 1.58 s, 4577 paths, 76559 anchors, 130 MB |
|
||||
| Technical Drawing | four-colour badge | 16 | 0.44 s, 1 paths, 69 anchors, 88 MB |
|
||||
| Outline | photo-like | 16 | 1.47 s, 632 paths, 34884 anchors, 256 MB |
|
||||
| Outline | four-colour badge | 16 | 0.75 s, 1 paths, 49 anchors, 176 MB |
|
||||
| Flat Logo | photo-like | 16 | refused after 0.20 s (not flat art), 40 MB |
|
||||
| Flat Logo | four-colour badge | 16 | 0.80 s, 5 paths, 67 anchors, 146 MB |
|
||||
| Default with Shapes | photo-like | 16 | 1.19 s, 127 paths, 34034 anchors, 199 MB |
|
||||
| Default with Shapes | four-colour badge | 16 | 0.54 s, 3 paths, 88 anchors, 184 MB |
|
||||
| 6 Colors with Shapes | photo-like | 16 | 1.61 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 16 | 1.02 s, 5 paths, 112 anchors, 185 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 16 | 1.32 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 16 | 0.91 s, 5 paths, 112 anchors, 184 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 16 | 1.72 s, 686 paths, 53654 anchors, 407 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 16 | 1.39 s, 9 paths, 2208 anchors, 270 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 16 | 6.71 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 16 | 1.42 s, 6 paths, 264 anchors, 257 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 16 | 1.78 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 16 | 0.89 s, 5 paths, 112 anchors, 184 MB |
|
||||
| 6 Colors with Gradients | photo-like | 16 | 3.33 s, 470 paths, 60831 anchors, 512 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 16 | 1.64 s, 5 paths, 112 anchors, 185 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 16 | 4.39 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 16 | 1.26 s, 6 paths, 264 anchors, 257 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 16 | 5.99 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 16 | 2.03 s, 6 paths, 264 anchors, 257 MB |
|
||||
| 6 Colors with Shapes | photo-like | 16 | 1.40 s, 470 paths, 60823 anchors, 298 MB |
|
||||
| 6 Colors with Shapes | four-colour badge | 16 | 1.00 s, 5 paths, 112 anchors, 185 MB |
|
||||
| 6 Colors with Auto Grouping | photo-like | 16 | 1.64 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors with Auto Grouping | four-colour badge | 16 | 1.04 s, 5 paths, 112 anchors, 185 MB |
|
||||
| Shades of Gray with Auto Grouping | photo-like | 16 | 2.19 s, 686 paths, 53654 anchors, 407 MB |
|
||||
| Shades of Gray with Auto Grouping | four-colour badge | 16 | 1.63 s, 9 paths, 2208 anchors, 270 MB |
|
||||
| High Fidelity Photo with Auto Grouping | photo-like | 16 | 9.72 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo with Auto Grouping | four-colour badge | 16 | 1.64 s, 6 paths, 264 anchors, 257 MB |
|
||||
| 6 Colors, no Gradients | photo-like | 16 | 1.66 s, 470 paths, 60831 anchors, 298 MB |
|
||||
| 6 Colors, no Gradients | four-colour badge | 16 | 1.13 s, 5 paths, 112 anchors, 184 MB |
|
||||
| 6 Colors with Gradients | photo-like | 16 | 3.71 s, 470 paths, 60831 anchors, 512 MB |
|
||||
| 6 Colors with Gradients | four-colour badge | 16 | 1.71 s, 5 paths, 112 anchors, 188 MB |
|
||||
| High Fidelity Photo, no Gradients | photo-like | 16 | 4.87 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo, no Gradients | four-colour badge | 16 | 1.50 s, 6 paths, 264 anchors, 257 MB |
|
||||
| High Fidelity Photo with Gradients | photo-like | 16 | 6.74 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo with Gradients | four-colour badge | 16 | 1.68 s, 6 paths, 264 anchors, 257 MB |
|
||||
| Default, enhanced | photo-like | 16 | 1.18 s, 127 paths, 34034 anchors, 199 MB |
|
||||
| Default, enhanced | four-colour badge | 16 | 0.57 s, 3 paths, 88 anchors, 184 MB |
|
||||
| Auto Color, enhanced | photo-like | 16 | 2.31 s, 5390 paths, 454626 anchors, 283 MB |
|
||||
| Auto Color, enhanced | four-colour badge | 16 | 0.98 s, 5 paths, 112 anchors, 188 MB |
|
||||
| High Fidelity Photo, enhanced | photo-like | 16 | 9.09 s, 31139 paths, 1192715 anchors, 880 MB |
|
||||
| High Fidelity Photo, enhanced | four-colour badge | 16 | 1.47 s, 6 paths, 264 anchors, 263 MB |
|
||||
| Low Fidelity Photo, enhanced | photo-like | 16 | 2.70 s, 2036 paths, 182770 anchors, 754 MB |
|
||||
| Low Fidelity Photo, enhanced | four-colour badge | 16 | 1.60 s, 6 paths, 149 anchors, 257 MB |
|
||||
| 3 Colors, enhanced | photo-like | 16 | 1.82 s, 76 paths, 23019 anchors, 290 MB |
|
||||
| 3 Colors, enhanced | four-colour badge | 16 | 1.24 s, 4 paths, 98 anchors, 205 MB |
|
||||
| 6 Colors, enhanced | photo-like | 16 | 2.20 s, 470 paths, 60823 anchors, 298 MB |
|
||||
| 6 Colors, enhanced | four-colour badge | 16 | 2.19 s, 5 paths, 112 anchors, 185 MB |
|
||||
| 16 Colors, enhanced | photo-like | 16 | 2.03 s, 1973 paths, 145623 anchors, 303 MB |
|
||||
| 16 Colors, enhanced | four-colour badge | 16 | 1.13 s, 5 paths, 114 anchors, 188 MB |
|
||||
| Shades of Gray, enhanced | photo-like | 16 | 2.76 s, 686 paths, 53648 anchors, 407 MB |
|
||||
| Shades of Gray, enhanced | four-colour badge | 16 | 3.36 s, 9 paths, 1989 anchors, 270 MB |
|
||||
| Black and White Logo, enhanced | photo-like | 16 | 1.58 s, 1128 paths, 45272 anchors, 129 MB |
|
||||
| Black and White Logo, enhanced | four-colour badge | 16 | 0.59 s, 1 paths, 69 anchors, 88 MB |
|
||||
| Sketched Art, enhanced | photo-like | 16 | 1.52 s, 15 paths, 11486 anchors, 128 MB |
|
||||
| Sketched Art, enhanced | four-colour badge | 16 | 0.54 s, 2 paths, 44 anchors, 88 MB |
|
||||
| Silhouettes, enhanced | photo-like | 16 | 1.82 s, 3 paths, 3350 anchors, 144 MB |
|
||||
| Silhouettes, enhanced | four-colour badge | 16 | 0.56 s, 1 paths, 8 anchors, 95 MB |
|
||||
| Line Art, enhanced | photo-like | 16 | 1.43 s, 713 paths, 37370 anchors, 129 MB |
|
||||
| Line Art, enhanced | four-colour badge | 16 | 0.58 s, 1 paths, 49 anchors, 88 MB |
|
||||
| Technical Drawing, enhanced | photo-like | 16 | 1.76 s, 4577 paths, 76558 anchors, 130 MB |
|
||||
| Technical Drawing, enhanced | four-colour badge | 16 | 0.52 s, 1 paths, 69 anchors, 88 MB |
|
||||
| Outline, enhanced | photo-like | 16 | 1.64 s, 632 paths, 34884 anchors, 256 MB |
|
||||
| Outline, enhanced | four-colour badge | 16 | 0.73 s, 1 paths, 49 anchors, 176 MB |
|
||||
|
||||
## Revision history
|
||||
|
||||
| date | change |
|
||||
|:--|:--|
|
||||
| 2026-10-14 | Gradients: a gradient that noise or a soft rim broke into pieces is found in its largest piece and grown over the rest (blurred and noisy linear gradients: 3 to 11 paths beside the gradient down to 0.5 with 6 Colors, 24 to 56 down to 0.5 to 1.5 with High Fidelity Photo) |
|
||||
| 2026-10-14 | Enhanced versions of the presets (Auto Grouping, and Shapes in all but High Fidelity Photo; Flat Logo has none), each measured beside its preset; a hole that one colour fills is the same shape as what fills it, rectangles are sized by where their sides lie, and a group keeps its paths in the order they were traced in (grouped, a letter with a counter was painted with a light seam: Default with Auto Grouping, edge ΔE 10.24 to 9.88) |
|
||||
| 2026-10-14 | Create: Strokes: a line whose ink is cut square at its ends gets square caps, and a wide line whose skeleton hooks towards a corner of its end ends on the line it was following (Outline: largest distance 2.94 px to 0.88; aliased lines 0.88 to 0.81) |
|
||||
| 2026-10-13 | Shapes: corners that anti-aliasing or blur took a little off are corners, a rounded rectangle gets the radius that fits, a polygon its sides from the outline itself, hard-edged outlines a little more room (plain shapes found: 57 to 62% up to 98 to 100%); a "false shapes" column |
|
||||
| 2026-10-13 | Flat Logo: the tones a scan's uneven paper makes of one colour are one colour (scans: 470 paths and 21 px off to 8.5 paths and 0.09 px) |
|
||||
|
||||
Reference in new issue
Block a user