1#![forbid(unsafe_code)]
10#![warn(missing_docs)]
11#![allow(clippy::field_reassign_with_default)]
12#![allow(clippy::identity_op)]
13#![allow(clippy::too_many_arguments)]
14#![allow(clippy::uninlined_format_args)]
15#![allow(clippy::upper_case_acronyms)]
16#![allow(clippy::wrong_self_convention)]
17
18pub use tiny_skia;
19pub use usvg;
20
21mod clip;
22mod filter;
23mod geom;
24mod image;
25mod mask;
26mod path;
27mod render;
28
29pub fn render(
36 tree: &usvg::Tree,
37 transform: tiny_skia::Transform,
38 pixmap: &mut tiny_skia::PixmapMut,
39) {
40 let target_size = tiny_skia::IntSize::from_wh(pixmap.width(), pixmap.height()).unwrap();
41 let max_bbox = tiny_skia::IntRect::from_xywh(
42 -(target_size.width() as i32) * 2,
43 -(target_size.height() as i32) * 2,
44 target_size.width() * 4,
45 target_size.height() * 4,
46 )
47 .unwrap();
48
49 let ctx = render::Context { max_bbox };
50 render::render_nodes(tree.root(), &ctx, transform, pixmap);
51}
52
53pub fn render_node(
64 node: &usvg::Node,
65 mut transform: tiny_skia::Transform,
66 pixmap: &mut tiny_skia::PixmapMut,
67) -> Option<()> {
68 let bbox = node.abs_layer_bounding_box()?;
69
70 let target_size = tiny_skia::IntSize::from_wh(pixmap.width(), pixmap.height()).unwrap();
71 let max_bbox = tiny_skia::IntRect::from_xywh(
72 -(target_size.width() as i32) * 2,
73 -(target_size.height() as i32) * 2,
74 target_size.width() * 4,
75 target_size.height() * 4,
76 )
77 .unwrap();
78
79 transform = transform.pre_translate(-bbox.x(), -bbox.y());
80
81 let ctx = render::Context { max_bbox };
82 render::render_node(node, &ctx, transform, pixmap);
83
84 Some(())
85}
86
87pub(crate) trait OptionLog {
88 fn log_none<F: FnOnce()>(self, f: F) -> Self;
89}
90
91impl<T> OptionLog for Option<T> {
92 #[inline]
93 fn log_none<F: FnOnce()>(self, f: F) -> Self {
94 self.or_else(|| {
95 f();
96 None
97 })
98 }
99}