the aliasing svg renderer
add logging
| -rw-r--r-- | Cargo.toml | 4 | ||||
| -rw-r--r-- | src/logger.rs | 40 | ||||
| -rw-r--r-- | src/main.rs | 30 | ||||
| -rw-r--r-- | src/render.rs | 12 | ||||
| -rw-r--r-- | src/tree.rs | 99 |
5 files changed, 145 insertions, 40 deletions
@@ -8,9 +8,13 @@ edition = "2021" [dependencies] anyhow = "1.0.75" clap = { version = "4.4.6", features = ["derive"] } +comat = "0.1.3" fimg = "0.4.16" +humantime = "2.1.0" +log = "0.4.20" tiny-skia-path = "0.11.2" usvg = { version = "0.36.0", default-features = false } +vecto = "0.1.1" [profile.release] debug = 2 diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..52f11c9 --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,40 @@ +use comat::{cformat_args, cprintln}; +use log::{Level, Metadata, Record}; + +pub struct Logger {} + +pub fn init(level: Level) { + static LOGGER: Logger = Logger {}; + log::set_logger(&LOGGER) + .map(|()| log::set_max_level(level.to_level_filter())) + .unwrap(); +} + +impl log::Log for Logger { + fn enabled(&self, _: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + match record.level() { + Level::Info => println!("{}", record.args()), + l => { + cprintln!( + "[{} {:bold_blue}:{:blue}] {}", + match l { + Level::Error => cformat_args!("{bold_red}err{reset}"), + Level::Warn => cformat_args!("{bold_yellow}wrn{reset}"), + Level::Trace => cformat_args!("{magenta}trc{reset}"), + Level::Debug => cformat_args!("{green}dbg{reset}"), + Level::Info => cformat_args!("{blue}inf{reset}"), + }, + record.file().unwrap_or("<source>"), + record.line().unwrap_or(0), + record.args(), + ) + } + } + } + + fn flush(&self) {} +} diff --git a/src/main.rs b/src/main.rs index e85bc71..079bb37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ -use std::{path::PathBuf, str::FromStr}; - use anyhow::{anyhow, Result}; use clap::Parser; +use log::Level as RLevel; use psvg::tree::Tree; +use std::{path::PathBuf, str::FromStr}; +mod logger; #[derive(Parser)] @@ -13,12 +14,32 @@ struct Args { /// If not supplied, will render at the svg's set width and height. /// Specify as: '144x124' size: Option<Size>, + /// Log level. + #[arg(default_value = "info", long = "log")] + log: Level, /// Svg to render. file: PathBuf, /// File to output rendered svg (png). out: PathBuf, } +#[repr(usize)] +#[derive(clap::ValueEnum, Clone, Copy)] +enum Level { + Error = 1, + Warn, + Info, + Debug, + Trace, +} + +impl From<Level> for RLevel { + fn from(value: Level) -> Self { + // SAFETY: same + unsafe { std::mem::transmute(value) } + } +} + #[derive(Copy, Clone, Debug, Default)] struct Size { w: u32, @@ -41,12 +62,13 @@ impl FromStr for Size { fn main() -> Result<()> { let args = Args::parse(); - let mut svg = Tree::new(&std::fs::read_to_string(args.file)?)?; + logger::init(args.log.into()); + let (mut svg, size) = Tree::new(&std::fs::read_to_string(args.file)?)?; match args.size { Some(Size { w, h }) => { svg.resize(w as f32, h as f32); } - None => svg.resize(svg.osize.width(), svg.osize.height()), + None => svg.resize(size.width(), size.height()), } svg.render().save(args.out); Ok(()) diff --git a/src/render.rs b/src/render.rs index bca39b5..3abe417 100644 --- a/src/render.rs +++ b/src/render.rs @@ -1,4 +1,4 @@ -use crate::tree::{Node, PathNode, Tree}; +use crate::tree::{Node, Tree}; use fimg::Image; use tiny_skia_path::{NormalizedF32, Point}; use usvg::Color; @@ -6,6 +6,7 @@ use usvg::Color; impl Tree { #[must_use] pub fn render(&self) -> Image<Box<[u8]>, 4> { + log::trace!("rendering {self:#?}"); let mut canvas = Image::alloc(self.width.round() as u32, self.height.round() as u32).boxed(); for node in &*self.children { @@ -36,20 +37,19 @@ fn point(p: &Box<[Point]>) -> Vec<(i32, i32)> { fn render(node: &Node, img: &mut Image<Box<[u8]>, 4>) { match node { - Node::Path(PathNode::Fill { + Node::Fill { color, opacity, path, - }) => { + } => { img.points(&point(path), color.col(*opacity)); } - Node::Path(PathNode::Stroke { + Node::Stroke { color, opacity, path, .. // TODO: stroek - }) => img.points(&point(path), color.col(*opacity)), - t => unimplemented!("{t:?}"), + } => img.points(&point(path), color.col(*opacity)), } } diff --git a/src/tree.rs b/src/tree.rs index 23284a2..9dce2a8 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -1,18 +1,66 @@ use anyhow::Result; use std::rc::Rc; use tiny_skia_path::{NonZeroPositiveF32, NormalizedF32, Path, Point, Size, Transform}; -use usvg::{Color, Fill, Image, Opacity, Options, Paint, TreeParsing}; +use usvg::{Color, Fill, Opacity, Options, Paint, TreeParsing}; +use vecto::Vector2; #[derive(Debug)] pub struct Tree { pub width: f32, pub height: f32, pub children: Box<[Node]>, - pub osize: Size, } -#[derive(Debug)] -pub enum PathNode { +impl std::fmt::Debug for Node { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn pack(Color { red, green, blue }: Color, a: Opacity) -> u64 { + ((red as u64) << 24) + | ((green as u64) << 16) + | ((blue as u64) << 8) + | (a.to_u8() as u64) + } + + macro_rules! hex { + ($c:expr, $a:expr) => { + &format_args!("#{:x}", pack($c, $a)) + }; + } + // can be transmuted etc but takes much effort + fn vec(points: &[Point]) -> Vec<Vector2<u64>> { + points + .iter() + .map(|&Point { x, y }| Vector2::new(x.round() as u64, y.round() as u64)) + .collect() + } + match self { + Self::Fill { + color, + opacity, + path, + } => f + .debug_struct("Fill") + .field("color", hex!(*color, *opacity)) + .field("path", &format_args!("{:?}", vec(path))) + .finish(), + Self::Stroke { + color, + opacity, + stroke_color, + stroke_opacity, + stroke, + path, + } => f + .debug_struct("Stroke") + .field("color", hex!(*color, *opacity)) + .field("stroke_color", hex!(*stroke_color, *stroke_opacity)) + .field("stroke", &stroke.get()) + .field("path", &format_args!("{:?}", vec(path))) + .finish(), + } + } +} + +pub enum Node { Fill { color: Color, opacity: Opacity, @@ -28,12 +76,6 @@ pub enum PathNode { }, } -#[derive(Debug)] -pub enum Node { - Path(PathNode), - Image(Image), -} - fn pointify(p: &Rc<Path>, t: Transform) -> Box<[Point]> { let mut p: Box<[Point]> = p.points().into(); t.map_points(&mut p); @@ -42,16 +84,11 @@ fn pointify(p: &Rc<Path>, t: Transform) -> Box<[Point]> { impl Node { fn transform(&mut self, t: Transform) { - match self { - Self::Path(PathNode::Fill { path, .. } | PathNode::Stroke { path, .. }) => { - if t.is_identity() { - return; - } - t.map_points(path); - } - // TODO - _ => {} + let (Self::Fill { path, .. } | Self::Stroke { path, .. }) = self; + if t.is_identity() { + return; } + t.map_points(path); } } @@ -60,7 +97,6 @@ impl From<usvg::Tree> for Tree { let mut children = vec![]; collect(tree.root, &mut children); Self { - osize: tree.size, width: tree.view_box.rect.width(), height: tree.view_box.rect.height(), children: children.into(), @@ -95,14 +131,14 @@ fn convert(node: usvg::Node, to: &mut Vec<Node>) { data: path, .. }) => { - to.push(Node::Path(PathNode::Stroke { + to.push(Node::Stroke { color: paint.col(), opacity: *opacity, stroke: *stroke, stroke_opacity: *stroke_opacity, stroke_color: stroke_paint.col(), path: pointify(path, *transform), - })); + }); } usvg::NodeKind::Path(usvg::Path { stroke: @@ -117,14 +153,14 @@ fn convert(node: usvg::Node, to: &mut Vec<Node>) { data: path, .. }) => { - to.push(Node::Path(PathNode::Stroke { + to.push(Node::Stroke { color: Color::black(), opacity: NormalizedF32::new(0.0).unwrap(), stroke: *stroke, stroke_opacity: *stroke_opacity, stroke_color: stroke_paint.col(), path: pointify(path, *transform), - })); + }); } usvg::NodeKind::Path(usvg::Path { transform, @@ -133,11 +169,11 @@ fn convert(node: usvg::Node, to: &mut Vec<Node>) { data: path, .. }) => { - to.push(Node::Path(PathNode::Fill { + to.push(Node::Fill { color: paint.col(), opacity: *opacity, path: pointify(path, *transform), - })); + }); } usvg::NodeKind::Path(usvg::Path { transform, @@ -146,11 +182,11 @@ fn convert(node: usvg::Node, to: &mut Vec<Node>) { data: path, .. }) => { - to.push(Node::Path(PathNode::Fill { + to.push(Node::Fill { color: Color::black(), opacity: NormalizedF32::new(0.0).unwrap(), path: pointify(path, *transform), - })); + }); } usvg::NodeKind::Group(_) => {} usvg::NodeKind::Image(_) => todo!(), @@ -166,8 +202,10 @@ fn collect(node: usvg::Node, to: &mut Vec<Node>) { } impl Tree { - pub fn new(svg: &str) -> Result<Self> { - Ok(Tree::from(usvg::Tree::from_str(svg, &Options::default())?)) + pub fn new(svg: &str) -> Result<(Self, Size)> { + let t = usvg::Tree::from_str(svg, &Options::default())?; + let ts = t.size; + Ok((Tree::from(t), ts)) } pub fn resize(&mut self, w: f32, h: f32) { @@ -177,5 +215,6 @@ impl Tree { } self.width = w; self.height = h; + log::debug!("changed size to {w}x{h}"); } } |