Skip to content

Commit

Permalink
build(deps): 🤖 update usvg to 0.44.0
Browse files Browse the repository at this point in the history
  • Loading branch information
M-Adoo committed Nov 18, 2024
1 parent 6e25ae9 commit bce7a40
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 146 deletions.
6 changes: 2 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ euclid = "0.22.6"
fontdb = "0.23.0"
futures = "0.3.26"
guillotiere = "0.6.0"
image = { version = "0.24.5", default-features = false }
image = { version = "0.24.5" }
indextree = "4.7.3"
log = "0.4.14"
lyon_algorithms = "1.0.1"
Expand All @@ -65,7 +65,6 @@ pin-project-lite = "0.2.15"
proc-macro2 = "1.0.89"
quote = "1.0.37"
rayon = "1.10.0"
rctree = "0.5.0"
rustybuzz = "0.20.1"
rxrust = { version="1.0.0-beta.9", default-features = false, features = ["futures-scheduler"]}
scoped_threadpool = "0.1.9"
Expand All @@ -74,11 +73,10 @@ serde = "1.0"
serde_json = "1.0.82"
smallvec = "1.8.0"
syn = "2.0.87"
tiny-skia-path = {version = "0.11.4"}
unicode-bidi = "0.3.7"
unicode-script = "0.5.4"
unicode-segmentation = "1.9.0"
usvg = { version= "0.36.0", default-features = false }
usvg = { version= "0.44.0", default-features = false }
webbrowser = "0.8.8"
wgpu = {version = "0.20.0", features=["webgl"]}
winit = { version="0.29.5", default-features = false, features = ["x11", "wayland", "wayland-dlopen", "rwh_06"]}
Expand Down
2 changes: 0 additions & 2 deletions painter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ log.workspace = true
lyon_algorithms = {version = "1.0.3", features = ["serialization"]}
lyon_tessellation = {version = "1.0.3", features = ["serialization"], optional = true}
material-color-utilities-rs = {workspace = true}
rctree.workspace = true
ribir_algo = {path = "../algo", version = "0.4.0-alpha.15" }
ribir_geom = {path = "../geom", version = "0.4.0-alpha.15" }
serde = {version = "1.0", features = ["derive"]}
serde_json.workspace = true
tiny-skia-path = {workspace = true}
usvg.workspace = true
zerocopy = {workspace = true, optional = true, features = ["derive"]}
derive_more= {workspace = true, features = ["add", "add_assign", "not", "mul"]}
Expand Down
1 change: 1 addition & 0 deletions painter/src/path_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use lyon_algorithms::path::{
path::Builder as LyonBuilder,
};
use ribir_geom::{Angle, Point, Rect, Transform, Vector};
use usvg::tiny_skia_path;

use crate::{LineCap, LineJoin, Path, Radius, StrokeOptions};

Expand Down
251 changes: 111 additions & 140 deletions painter/src/svg.rs
Original file line number Diff line number Diff line change
@@ -1,135 +1,37 @@
use std::{error::Error, io::Read};
use std::{error::Error, io::Read, vec};

use ribir_algo::Resource;
use ribir_geom::{Point, Rect, Size, Transform};
use serde::{Deserialize, Serialize};
use usvg::{Options, Stop, Tree, TreeParsing};
use usvg::{Options, Stop, Tree};

use crate::{
Brush, Color, GradientStop, LineCap, LineJoin, PaintCommand, Path, StrokeOptions,
color::{LinearGradient, RadialGradient},
};

/// This is a basic SVG support designed for rendering to Ribir painter. It is
/// currently quite simple and primarily used for Ribir icons. More features
/// will be added as needed.
#[derive(Serialize, Deserialize, Clone)]
pub struct Svg {
pub size: Size,
pub commands: Resource<Box<[PaintCommand]>>,
}

/// Fits size into a viewbox. copy from resvg
fn fit_view_box(size: usvg::Size, vb: &usvg::ViewBox) -> usvg::Size {
let s = vb.rect.size();

if vb.aspect.align == usvg::Align::None {
s
} else if vb.aspect.slice {
size.expand_to(s)
} else {
size.scale_to(s)
}
}

// todo: we need to support currentColor to change svg color.
// todo: share fontdb
impl Svg {
pub fn parse_from_bytes(svg_data: &[u8]) -> Result<Self, Box<dyn Error>> {
let opt = Options { ..<_>::default() };
let tree = Tree::from_data(svg_data, &opt).unwrap();
let view_rect = tree.view_box.rect;
let size = tree.size;
let fit_size = fit_view_box(size, &tree.view_box);

let bound_rect = Rect::from_size(Size::new(f32::MAX, f32::MAX));
let size = tree.size();

let bound_rect = Rect::from_size(Size::new(f32::MAX, f32::MAX));
let mut painter = crate::Painter::new(bound_rect);
painter.apply_transform(
&Transform::translation(-view_rect.x(), -view_rect.y())
.then_scale(size.width() / fit_size.width(), size.height() / fit_size.height()),
);
tree.root.traverse().for_each(|edge| match edge {
rctree::NodeEdge::Start(node) => {
use usvg::NodeKind;
painter.save();
match &*node.borrow() {
NodeKind::Path(p) => {
painter.apply_transform(&matrix_convert(p.transform));
let path = usvg_path_to_path(p);
if let Some(ref fill) = p.fill {
let (brush, transform) = brush_from_usvg_paint(&fill.paint, fill.opacity, &size);
let mut painter = painter.save_guard();

let inverse_ts = transform.inverse().unwrap();
let path = Resource::new(path.clone().transform(&inverse_ts));
painter
.set_fill_brush(brush.clone())
.apply_transform(&transform)
.fill_path(path.into());
//&o_ts.then(&n_ts.inverse().unwrap())));
}

if let Some(ref stroke) = p.stroke {
let cap = match stroke.linecap {
usvg::LineCap::Butt => LineCap::Butt,
usvg::LineCap::Square => LineCap::Square,
usvg::LineCap::Round => LineCap::Round,
};
let join = match stroke.linejoin {
usvg::LineJoin::Miter => LineJoin::Miter,
usvg::LineJoin::Bevel => LineJoin::Bevel,
usvg::LineJoin::Round => LineJoin::Round,
usvg::LineJoin::MiterClip => LineJoin::MiterClip,
};
let options = StrokeOptions {
width: stroke.width.get(),
line_cap: cap,
line_join: join,
miter_limit: stroke.miterlimit.get(),
};

let (brush, transform) = brush_from_usvg_paint(&stroke.paint, stroke.opacity, &size);
let mut painter = painter.save_guard();

painter
.set_stroke_brush(brush.clone())
.apply_transform(&transform);

let path = path
.transform(&transform.inverse().unwrap())
.stroke(&options, Some(painter.transform()));

if let Some(p) = path {
painter.fill_path(Resource::new(p).into());
}
};
}
NodeKind::Image(_) => {
// todo;
log::warn!("[painter]: not support draw embed image in svg, ignored!");
}
NodeKind::Group(ref g) => {
painter.apply_transform(&matrix_convert(g.transform));
// todo;
if g.opacity.get() != 1. {
log::warn!("[painter]: not support `opacity` in svg, ignored!");
}
if g.clip_path.is_some() {
log::warn!("[painter]: not support `clip path` in svg, ignored!");
}
if g.mask.is_some() {
log::warn!("[painter]: not support `mask` in svg, ignored!");
}
if !g.filters.is_empty() {
log::warn!("[painter]: not support `filters` in svg, ignored!");
}
}
NodeKind::Text(_) => {
todo!("Not support text in SVG temporarily, we'll add it after refactoring `painter`.")
}
}
}
rctree::NodeEdge::End(_) => {
painter.restore();
}
});
paint_group(tree.root(), &mut painter);

let paint_commands = painter.finish().to_owned().into_boxed_slice();

Expand All @@ -154,9 +56,73 @@ impl Svg {
pub fn deserialize(str: &str) -> Result<Self, Box<dyn Error>> { Ok(serde_json::from_str(str)?) }
}

fn paint_group(g: &usvg::Group, painter: &mut crate::Painter) {
let mut painter = painter.save_guard();
for child in g.children() {
match child {
usvg::Node::Group(g) => {
// todo;
painter.apply_alpha(g.opacity().get());

if g.clip_path().is_some() {
log::warn!("[painter]: not support `clip path` in svg, ignored!");
}
if g.mask().is_some() {
log::warn!("[painter]: not support `mask` in svg, ignored!");
}
if !g.filters().is_empty() {
log::warn!("[painter]: not support `filters` in svg, ignored!");
}
paint_group(g, &mut painter);
}
usvg::Node::Path(p) => {
painter.set_transform(matrix_convert(p.abs_transform()));
let path = usvg_path_to_path(p);
if let Some(fill) = p.fill() {
let (brush, transform) = brush_from_usvg_paint(fill.paint(), fill.opacity());

let inverse_ts = transform.inverse().unwrap();
let path = Resource::new(path.clone().transform(&inverse_ts));
painter
.set_fill_brush(brush.clone())
.apply_transform(&transform)
.fill_path(path.into());
//&o_ts.then(&n_ts.inverse().unwrap())));
}

if let Some(stroke) = p.stroke() {
let options = StrokeOptions {
width: stroke.width().get(),
line_cap: stroke.linecap().into(),
line_join: stroke.linejoin().into(),
miter_limit: stroke.miterlimit().get(),
};

let (brush, transform) = brush_from_usvg_paint(stroke.paint(), stroke.opacity());
painter
.set_stroke_brush(brush.clone())
.apply_transform(&transform);

let path = path
.transform(&transform.inverse().unwrap())
.stroke(&options, Some(painter.transform()));

if let Some(p) = path {
painter.fill_path(Resource::new(p).into());
}
};
}
usvg::Node::Image(_) => {
// todo;
log::warn!("[painter]: not support draw embed image in svg, ignored!");
}
usvg::Node::Text(t) => paint_group(t.flattened(), &mut painter),
}
}
}
fn usvg_path_to_path(path: &usvg::Path) -> Path {
let mut builder = lyon_algorithms::path::Path::svg_builder();
path.data.segments().for_each(|seg| match seg {
path.data().segments().for_each(|seg| match seg {
usvg::tiny_skia_path::PathSegment::MoveTo(pt) => {
builder.move_to(point(pt.x, pt.y));
}
Expand All @@ -182,9 +148,7 @@ fn matrix_convert(t: usvg::Transform) -> Transform {
Transform::new(sx, ky, kx, sy, tx, ty)
}

fn brush_from_usvg_paint(
paint: &usvg::Paint, opacity: usvg::Opacity, size: &usvg::Size,
) -> (Brush, Transform) {
fn brush_from_usvg_paint(paint: &usvg::Paint, opacity: usvg::Opacity) -> (Brush, Transform) {
match paint {
usvg::Paint::Color(usvg::Color { red, green, blue }) => (
Color::from_rgb(*red, *green, *blue)
Expand All @@ -193,42 +157,28 @@ fn brush_from_usvg_paint(
Transform::identity(),
),
usvg::Paint::LinearGradient(linear) => {
let stops = convert_to_gradient_stops(&linear.stops);
let size_scale = match linear.units {
usvg::Units::UserSpaceOnUse => (1., 1.),
usvg::Units::ObjectBoundingBox => (size.width(), size.height()),
};
let stops = convert_to_gradient_stops(linear.stops());
let gradient = LinearGradient {
start: Point::new(linear.x1 * size_scale.0, linear.y1 * size_scale.1),
end: Point::new(linear.x2 * size_scale.0, linear.y2 * size_scale.1),
start: Point::new(linear.x1(), linear.y1()),
end: Point::new(linear.x2(), linear.y2()),
stops,
spread_method: linear.spread_method.into(),
spread_method: linear.spread_method().into(),
};

(Brush::LinearGradient(gradient), matrix_convert(linear.transform))
(Brush::LinearGradient(gradient), matrix_convert(linear.transform()))
}
usvg::Paint::RadialGradient(radial_gradient) => {
let stops = convert_to_gradient_stops(&radial_gradient.stops);
let size_scale = match radial_gradient.units {
usvg::Units::UserSpaceOnUse => (1., 1.),
usvg::Units::ObjectBoundingBox => (size.width(), size.height()),
};
let stops = convert_to_gradient_stops(radial_gradient.stops());
let gradient = RadialGradient {
start_center: Point::new(
radial_gradient.fx * size_scale.0,
radial_gradient.fy * size_scale.1,
),
start_center: Point::new(radial_gradient.fx(), radial_gradient.fy()),
start_radius: 0., // usvg not support fr
end_center: Point::new(
radial_gradient.cx * size_scale.0,
radial_gradient.cy * size_scale.1,
),
end_radius: radial_gradient.r.get() * size_scale.0,
end_center: Point::new(radial_gradient.cx(), radial_gradient.cy()),
end_radius: radial_gradient.r().get(),
stops,
spread_method: radial_gradient.spread_method.into(),
spread_method: radial_gradient.spread_method().into(),
};

(Brush::RadialGradient(gradient), matrix_convert(radial_gradient.transform))
(Brush::RadialGradient(gradient), matrix_convert(radial_gradient.transform()))
}
paint => {
log::warn!("[painter]: not support `{paint:?}` in svg, use black instead!");
Expand All @@ -243,10 +193,10 @@ fn convert_to_gradient_stops(stops: &[Stop]) -> Vec<GradientStop> {
let mut stops: Vec<_> = stops
.iter()
.map(|stop| {
let usvg::Color { red, green, blue } = stop.color;
let usvg::Color { red, green, blue } = stop.color();
GradientStop {
offset: stop.offset.get(),
color: Color::new(red, green, blue, stop.opacity.to_u8()),
offset: stop.offset().get(),
color: Color::new(red, green, blue, stop.opacity().to_u8()),
}
})
.collect();
Expand All @@ -265,3 +215,24 @@ fn convert_to_gradient_stops(stops: &[Stop]) -> Vec<GradientStop> {
}
stops
}

impl From<usvg::LineCap> for LineCap {
fn from(value: usvg::LineCap) -> Self {
match value {
usvg::LineCap::Butt => LineCap::Butt,
usvg::LineCap::Round => LineCap::Round,
usvg::LineCap::Square => LineCap::Square,
}
}
}

impl From<usvg::LineJoin> for LineJoin {
fn from(value: usvg::LineJoin) -> Self {
match value {
usvg::LineJoin::Miter => LineJoin::Miter,
usvg::LineJoin::MiterClip => LineJoin::MiterClip,
usvg::LineJoin::Round => LineJoin::Round,
usvg::LineJoin::Bevel => LineJoin::Bevel,
}
}
}

0 comments on commit bce7a40

Please sign in to comment.