1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5/*!
6[resvg](https://github.com/RazrFalcon/resvg) is an SVG rendering library.
7*/
8
9#![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
29/// Renders a tree onto the pixmap.
30///
31/// `transform` will be used as a root transform.
32/// Can be used to position SVG inside the `pixmap`.
33///
34/// The produced content is in the sRGB color space.
35pub fn render(
36 tree: &usvg::Tree,
37 transform: tiny_skia::Transform,
38 pixmap: &mut tiny_skia::PixmapMut,
39) {
40 let target_size: IntSize = tiny_skia::IntSize::from_wh(pixmap.width(), pixmap.height()).unwrap();
41 let max_bbox: IntRect = tiny_skiaOption::IntRect::from_xywh(
42 -(target_size.width() as i32) * 2,
43 -(target_size.height() as i32) * 2,
44 width:target_size.width() * 4,
45 height:target_size.height() * 4,
46 )
47 .unwrap();
48
49 let ts: Transform = tree.view_box().to_transform(img_size:tree.size());
50 let root_transform: Transform = transform.pre_concat(ts);
51
52 let ctx: Context = render::Context { max_bbox };
53 render::render_nodes(parent:tree.root(), &ctx, root_transform, pixmap);
54}
55
56/// Renders a node onto the pixmap.
57///
58/// `transform` will be used as a root transform.
59/// Can be used to position SVG inside the `pixmap`.
60///
61/// The expected pixmap size can be retrieved from `usvg::Node::abs_layer_bounding_box()`.
62///
63/// Returns `None` when `node` has a zero size.
64///
65/// The produced content is in the sRGB color space.
66pub fn render_node(
67 node: &usvg::Node,
68 mut transform: tiny_skia::Transform,
69 pixmap: &mut tiny_skia::PixmapMut,
70) -> Option<()> {
71 let bbox: NonZeroRect = node.abs_layer_bounding_box()?;
72
73 let target_size: IntSize = tiny_skia::IntSize::from_wh(pixmap.width(), pixmap.height()).unwrap();
74 let max_bbox: IntRect = tiny_skiaOption::IntRect::from_xywh(
75 -(target_size.width() as i32) * 2,
76 -(target_size.height() as i32) * 2,
77 width:target_size.width() * 4,
78 height:target_size.height() * 4,
79 )
80 .unwrap();
81
82 transform = transform.pre_translate(-bbox.x(), -bbox.y());
83
84 let ctx: Context = render::Context { max_bbox };
85 render::render_node(node, &ctx, transform, pixmap);
86
87 Some(())
88}
89
90pub(crate) trait OptionLog {
91 fn log_none<F: FnOnce()>(self, f: F) -> Self;
92}
93
94impl<T> OptionLog for Option<T> {
95 #[inline]
96 fn log_none<F: FnOnce()>(self, f: F) -> Self {
97 self.or_else(|| {
98 f();
99 None
100 })
101 }
102}
103