the aliasing svg renderer
add logging
bendn 2023-10-18
parent 7693a30 · commit 13697af
-rw-r--r--Cargo.toml4
-rw-r--r--src/logger.rs40
-rw-r--r--src/main.rs30
-rw-r--r--src/render.rs12
-rw-r--r--src/tree.rs99
5 files changed, 145 insertions, 40 deletions
diff --git a/Cargo.toml b/Cargo.toml
index e838b6e..6d2864f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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}");
}
}