diff --git a/Cargo.lock b/Cargo.lock index 061d4b0..b19c078 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -307,6 +307,26 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", + "shlex", + "syn 2.0.117", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -5412,6 +5432,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + [[package]] name = "servo_arc" version = "0.2.0" @@ -5550,29 +5579,28 @@ checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "skia-bindings" -version = "0.82.0" +version = "0.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882318e42c24465c2b2140e9b2012c5d44217f680851aebb6884c64dee299156" +checksum = "3e2d1c3ebd697c0cbded0145e9204a38fa6b268446051b7196d0a096414ea7f3" dependencies = [ - "bindgen 0.71.1", + "bindgen 0.72.1", "cc", "flate2", "heck 0.5.0", - "lazy_static", + "pkg-config", "regex", "serde_json", "tar", - "toml", + "toml 1.1.4+spec-1.1.0", ] [[package]] name = "skia-safe" -version = "0.82.0" +version = "0.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db978a1042d9fb18a89617e349b1935af176068290b548dea2c89b49528aa361" +checksum = "9f512ac418a64194842dd05566320805dad1c957c521039db2486fd6368865bc" dependencies = [ "bitflags 2.11.0", - "lazy_static", "skia-bindings", ] @@ -6059,7 +6087,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.2", "version-compare", ] @@ -6127,9 +6155,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.44" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", @@ -6373,11 +6401,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ "serde", - "serde_spanned", + "serde_spanned 0.6.9", "toml_datetime 0.6.3", "toml_edit 0.20.2", ] +[[package]] +name = "toml" +version = "1.1.4+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.0", +] + [[package]] name = "toml_datetime" version = "0.6.3" @@ -6389,9 +6432,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -6415,7 +6458,7 @@ checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ "indexmap", "serde", - "serde_spanned", + "serde_spanned 0.6.9", "toml_datetime 0.6.3", "winnow 0.5.40", ] @@ -6427,20 +6470,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" dependencies = [ "indexmap", - "toml_datetime 1.1.0+spec-1.1.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "winnow 1.0.0", ] [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow 1.0.0", ] +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + [[package]] name = "tracing" version = "0.1.44" diff --git a/crates/rustmotion-components/Cargo.toml b/crates/rustmotion-components/Cargo.toml index 7ed52bd..381f5f7 100644 --- a/crates/rustmotion-components/Cargo.toml +++ b/crates/rustmotion-components/Cargo.toml @@ -15,7 +15,7 @@ lottie-native = ["dep:thorvg", "dep:dashmap"] [dependencies] rustmotion-core.workspace = true -skia-safe = "0.82" +skia-safe = "0.99" serde = { version = "1", features = ["derive"] } serde_json = "1" schemars = "0.8" diff --git a/crates/rustmotion-components/src/arrow.rs b/crates/rustmotion-components/src/arrow.rs index f014a12..e585886 100644 --- a/crates/rustmotion-components/src/arrow.rs +++ b/crates/rustmotion-components/src/arrow.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, PaintStyle, Path, PathMeasure, Point}; +use skia_safe::{Canvas, PaintStyle, Path, PathBuilder, PathMeasure, Point}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -87,7 +87,7 @@ rustmotion_core::impl_traits!(Arrow { impl Arrow { /// Build the bezier path for this arrow (without arrowheads). fn build_path(&self) -> Path { - let mut path = Path::new(); + let mut path = PathBuilder::new(); path.move_to((self.x1, self.y1)); if let (Some(cp1), Some(cp2)) = (&self.cp1, &self.cp2) { @@ -112,7 +112,7 @@ impl Arrow { path.line_to((self.x2, self.y2)); } - path + path.detach() } /// Draw an arrowhead at the given position along the path. @@ -146,7 +146,7 @@ impl Arrow { let angle = tangent.y.atan2(tangent.x); let half_angle = std::f32::consts::PI / 6.0; // 30 degrees - let mut arrow_path = Path::new(); + let mut arrow_path = PathBuilder::new(); arrow_path.move_to(pos); arrow_path.line_to(( pos.x - size * (angle - half_angle).cos(), @@ -161,7 +161,7 @@ impl Arrow { let mut arrow_paint = paint.clone(); arrow_paint.set_path_effect(None); arrow_paint.set_stroke_cap(skia_safe::PaintCap::Round); - canvas.draw_path(&arrow_path, &arrow_paint); + canvas.draw_path(&arrow_path.detach(), &arrow_paint); } fn paint(&self, canvas: &Canvas, props: &AnimatedProperties) { diff --git a/crates/rustmotion-components/src/callout.rs b/crates/rustmotion-components/src/callout.rs index 74cefae..22e8bf9 100644 --- a/crates/rustmotion-components/src/callout.rs +++ b/crates/rustmotion-components/src/callout.rs @@ -2,7 +2,7 @@ use rustmotion_core::css::CssStyle; use rustmotion_core::error::Result; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, PaintStyle, Path, RRect, Rect}; +use skia_safe::{Canvas, PaintStyle, Path, PathBuilder, RRect, Rect}; use rustmotion_core::engine::animator::AnimatedProperties; use rustmotion_core::engine::layout_pass::BoxLayout; @@ -89,7 +89,7 @@ impl Callout { } fn arrow_path(&self, w: f32, h: f32) -> Path { - let mut path = Path::new(); + let mut path = PathBuilder::new(); let a = self.arrow_size; // Overlap the arrow base 1px into the bubble to eliminate anti-aliasing seam let overlap = 1.0; @@ -129,7 +129,7 @@ impl Callout { } } - path + path.detach() } } diff --git a/crates/rustmotion-components/src/chart/funnel.rs b/crates/rustmotion-components/src/chart/funnel.rs index 60e9122..edc0fea 100644 --- a/crates/rustmotion-components/src/chart/funnel.rs +++ b/crates/rustmotion-components/src/chart/funnel.rs @@ -1,5 +1,5 @@ use rustmotion_core::error::Result; -use skia_safe::{Canvas, PaintStyle, Path}; +use skia_safe::{Canvas, PaintStyle, PathBuilder}; use rustmotion_core::engine::renderer::{ draw_text_with_fallback, emoji_typeface, measure_text_with_fallback, paint_from_hex, @@ -67,13 +67,13 @@ impl Chart { paint.set_style(PaintStyle::Fill); paint.set_anti_alias(true); - let mut path = Path::new(); + let mut path = PathBuilder::new(); path.move_to((top_x, y_top)); path.line_to((top_x + top_w, y_top)); path.line_to((bot_x + bot_w, y_bot)); path.line_to((bot_x, y_bot)); path.close(); - canvas.draw_path(&path, &paint); + canvas.draw_path(&path.detach(), &paint); if self.show_labels { if let Some(label) = &dp.label { @@ -148,13 +148,13 @@ impl Chart { paint.set_style(PaintStyle::Fill); paint.set_anti_alias(true); - let mut path = Path::new(); + let mut path = PathBuilder::new(); path.move_to((x_left, left_y)); path.line_to((x_right, right_y)); path.line_to((x_right, right_y + right_h)); path.line_to((x_left, left_y + left_h)); path.close(); - canvas.draw_path(&path, &paint); + canvas.draw_path(&path.detach(), &paint); if self.show_labels { if let Some(label) = &dp.label { diff --git a/crates/rustmotion-components/src/chart/line.rs b/crates/rustmotion-components/src/chart/line.rs index 16951d6..3230efb 100644 --- a/crates/rustmotion-components/src/chart/line.rs +++ b/crates/rustmotion-components/src/chart/line.rs @@ -1,5 +1,6 @@ use rustmotion_core::error::Result; -use skia_safe::{Canvas, Color, PaintStyle, Path, Point, Rect}; +use skia_safe::gradient::{self, Colors, Gradient}; +use skia_safe::{Canvas, Color, Color4f, PaintStyle, PathBuilder, Point, Rect}; use rustmotion_core::engine::renderer::{paint_from_hex, parse_hex_color}; @@ -67,8 +68,8 @@ impl Chart { return Ok(()); } - let mut path = Path::new(); - let mut fill_path = Path::new(); + let mut path = PathBuilder::new(); + let mut fill_path = PathBuilder::new(); for (i, dp) in self.data.iter().enumerate() { let x = ml + (i as f32 / (n - 1) as f32) * chart_w; @@ -102,14 +103,14 @@ impl Chart { let mut fill_paint = paint_from_hex(line_color); fill_paint.set_style(PaintStyle::Fill); fill_paint.set_alpha_f(0.15); - canvas.draw_path(&fill_path, &fill_paint); + canvas.draw_path(&fill_path.detach(), &fill_paint); // Line stroke let mut line_paint = paint_from_hex(line_color); line_paint.set_style(PaintStyle::Stroke); line_paint.set_stroke_width(2.5); line_paint.set_anti_alias(true); - canvas.draw_path(&path, &line_paint); + canvas.draw_path(&path.detach(), &line_paint); // Dots for (i, dp) in self.data.iter().enumerate() { @@ -168,8 +169,8 @@ impl Chart { }) .collect(); - let mut line_path = Path::new(); - let mut fill_path = Path::new(); + let mut line_path = PathBuilder::new(); + let mut fill_path = PathBuilder::new(); if self.smooth && pts.len() >= 3 { // Catmull-Rom -> cubic bezier for smooth curves @@ -227,12 +228,12 @@ impl Chart { let top_color = Color::from_argb((self.fill_opacity * 255.0) as u8, r, g, b); let bottom_color = Color::from_argb(0, r, g, b); - let shader = skia_safe::shader::Shader::linear_gradient( + let colors4f = [Color4f::from(top_color), Color4f::from(bottom_color)]; + let stops = Colors::new(&colors4f, None, skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(stops, gradient::Interpolation::default()); + let shader = gradient::shaders::linear_gradient( (Point::new(0.0, mt), Point::new(0.0, mt + chart_h)), - skia_safe::gradient_shader::GradientShaderColors::Colors(&[top_color, bottom_color]), - None, - skia_safe::TileMode::Clamp, - None, + &grad, None, ); @@ -241,7 +242,7 @@ impl Chart { fill_paint.set_style(PaintStyle::Fill); fill_paint.set_anti_alias(true); fill_paint.set_shader(shader); - canvas.draw_path(&fill_path, &fill_paint); + canvas.draw_path(&fill_path.detach(), &fill_paint); } // Line stroke @@ -249,7 +250,7 @@ impl Chart { line_paint.set_style(PaintStyle::Stroke); line_paint.set_stroke_width(2.5); line_paint.set_anti_alias(true); - canvas.draw_path(&line_path, &line_paint); + canvas.draw_path(&line_path.detach(), &line_paint); // Dots for &(x, y) in &pts { diff --git a/crates/rustmotion-components/src/chart/pie.rs b/crates/rustmotion-components/src/chart/pie.rs index 54065fb..b055ea8 100644 --- a/crates/rustmotion-components/src/chart/pie.rs +++ b/crates/rustmotion-components/src/chart/pie.rs @@ -1,5 +1,5 @@ use rustmotion_core::error::Result; -use skia_safe::{Canvas, PaintStyle, Path, Rect}; +use skia_safe::{Canvas, PaintStyle, PathBuilder, Rect}; use rustmotion_core::engine::renderer::paint_from_hex; @@ -93,7 +93,7 @@ impl Chart { canvas.draw_circle((cx, cy), outer_r, &paint); } } else { - let mut path = Path::new(); + let mut path = PathBuilder::new(); if inner_r > 0.0 { // Outer arc forward, inner arc back: a closed annulus sector. path.arc_to(outer, start_angle, sweep, false); @@ -103,7 +103,7 @@ impl Chart { path.arc_to(outer, start_angle, sweep, false); } path.close(); - canvas.draw_path(&path, &paint); + canvas.draw_path(&path.detach(), &paint); } start_angle += sweep; diff --git a/crates/rustmotion-components/src/chart/radar.rs b/crates/rustmotion-components/src/chart/radar.rs index de05e74..954ab5c 100644 --- a/crates/rustmotion-components/src/chart/radar.rs +++ b/crates/rustmotion-components/src/chart/radar.rs @@ -1,5 +1,5 @@ use rustmotion_core::error::Result; -use skia_safe::{Canvas, PaintStyle, Path}; +use skia_safe::{Canvas, PaintStyle, PathBuilder}; use rustmotion_core::engine::renderer::{ draw_text_with_fallback, emoji_typeface, measure_text_with_fallback, paint_from_hex, @@ -30,7 +30,7 @@ impl Chart { let grid_levels = 4; for level in 1..=grid_levels { let r = radius * (level as f32 / grid_levels as f32); - let mut grid_path = Path::new(); + let mut grid_path = PathBuilder::new(); for i in 0..n_axes { let angle = -std::f32::consts::FRAC_PI_2 + i as f32 * angle_step; let px = cx + r * angle.cos(); @@ -50,7 +50,7 @@ impl Chart { grid_paint.set_style(PaintStyle::Stroke); grid_paint.set_stroke_width(1.0); grid_paint.set_anti_alias(true); - canvas.draw_path(&grid_path, &grid_paint); + canvas.draw_path(&grid_path.detach(), &grid_paint); } // Draw axis lines @@ -120,7 +120,7 @@ impl Chart { let color_str = rd.color.as_deref().unwrap_or_else(|| self.get_color(di)); - let mut data_path = Path::new(); + let mut data_path = PathBuilder::new(); for (i, &val) in rd.values.iter().enumerate() { let norm = (val.max(0.0) / global_max) as f32 * progress; let angle = -std::f32::consts::FRAC_PI_2 + i as f32 * angle_step; @@ -140,14 +140,14 @@ impl Chart { fill_paint.set_style(PaintStyle::Fill); fill_paint.set_alpha_f(0.3); fill_paint.set_anti_alias(true); - canvas.draw_path(&data_path, &fill_paint); + canvas.draw_path(&data_path.snapshot(), &fill_paint); // Stroke let mut stroke_paint = paint_from_hex(color_str); stroke_paint.set_style(PaintStyle::Stroke); stroke_paint.set_stroke_width(2.0); stroke_paint.set_anti_alias(true); - canvas.draw_path(&data_path, &stroke_paint); + canvas.draw_path(&data_path.detach(), &stroke_paint); } Ok(()) diff --git a/crates/rustmotion-components/src/connector.rs b/crates/rustmotion-components/src/connector.rs index 1c0710f..68d4c58 100644 --- a/crates/rustmotion-components/src/connector.rs +++ b/crates/rustmotion-components/src/connector.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, PaintStyle, Path, PathMeasure, Point}; +use skia_safe::{Canvas, PaintStyle, Path, PathBuilder, PathMeasure, Point}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -93,7 +93,7 @@ rustmotion_core::impl_traits!(Connector { impl Connector { fn build_path(&self) -> Path { - let mut path = Path::new(); + let mut path = PathBuilder::new(); let (x1, y1) = (self.from.x, self.from.y); let (x2, y2) = (self.to.x, self.to.y); @@ -124,7 +124,7 @@ impl Connector { } } - path + path.detach() } fn draw_arrowhead( @@ -155,7 +155,7 @@ impl Connector { let angle = tangent.y.atan2(tangent.x); let half_angle = std::f32::consts::PI / 6.0; - let mut arrow_path = Path::new(); + let mut arrow_path = PathBuilder::new(); arrow_path.move_to(pos); arrow_path.line_to(( pos.x - size * (angle - half_angle).cos(), @@ -170,7 +170,7 @@ impl Connector { let mut arrow_paint = paint.clone(); arrow_paint.set_path_effect(None); arrow_paint.set_stroke_cap(skia_safe::PaintCap::Round); - canvas.draw_path(&arrow_path, &arrow_paint); + canvas.draw_path(&arrow_path.detach(), &arrow_paint); } fn paint(&self, canvas: &Canvas, props: &AnimatedProperties) { diff --git a/crates/rustmotion-components/src/divider.rs b/crates/rustmotion-components/src/divider.rs index 0350cb7..22f195d 100644 --- a/crates/rustmotion-components/src/divider.rs +++ b/crates/rustmotion-components/src/divider.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, PaintStyle, Path, Rect}; +use skia_safe::{Canvas, PaintStyle, PathBuilder, Rect}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::layout_pass::BoxLayout; @@ -96,7 +96,7 @@ impl Painter for Divider { paint.set_path_effect(effect); } - let mut path = Path::new(); + let mut path = PathBuilder::new(); if is_horizontal { let y = self.thickness / 2.0; path.move_to((0.0, y)); @@ -106,7 +106,7 @@ impl Painter for Divider { path.move_to((x, 0.0)); path.line_to((x, layout.height)); } - canvas.draw_path(&path, &paint); + canvas.draw_path(&path.detach(), &paint); } } } diff --git a/crates/rustmotion-components/src/gradient_text.rs b/crates/rustmotion-components/src/gradient_text.rs index ac11cc5..1311478 100644 --- a/crates/rustmotion-components/src/gradient_text.rs +++ b/crates/rustmotion-components/src/gradient_text.rs @@ -1,6 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, Font, FontStyle, Point}; +use skia_safe::gradient::{self, Colors, Gradient}; +use skia_safe::{Canvas, Color4f, Font, FontStyle, Point}; use rustmotion_core::css::style::{ FontStyle as CssFontStyle, FontWeight as CssFontWeight, FontWeightKw, @@ -215,14 +216,10 @@ impl GradientText { // Build linear gradient shader let positions: Option<&[f32]> = None; - let shader = skia_safe::shader::Shader::linear_gradient( - (start, end), - skia_safe::gradient_shader::GradientShaderColors::Colors(&skia_colors), - positions, - skia_safe::TileMode::Clamp, - None, - None, - ); + let colors4f: Vec = skia_colors.iter().map(|c| Color4f::from(*c)).collect(); + let stops = Colors::new(&colors4f, positions, skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(stops, gradient::Interpolation::default()); + let shader = gradient::shaders::linear_gradient((start, end), &grad, None); let fill_paint = match shader { Some(shader) => { diff --git a/crates/rustmotion-components/src/mockup.rs b/crates/rustmotion-components/src/mockup.rs index ad29189..8520a92 100644 --- a/crates/rustmotion-components/src/mockup.rs +++ b/crates/rustmotion-components/src/mockup.rs @@ -2,7 +2,7 @@ use rustmotion_core::css::CssStyle; use rustmotion_core::error::Result; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, Paint, PaintStyle, Path, RRect, Rect}; +use skia_safe::{Canvas, Paint, PaintStyle, PathBuilder, RRect, Rect}; use rustmotion_core::engine::animator::AnimatedProperties; use rustmotion_core::engine::layout_pass::BoxLayout; @@ -270,7 +270,7 @@ impl Mockup { // Base (trapezoid) let base_y = screen_h; let inset = w * 0.05; - let mut base_path = Path::new(); + let mut base_path = PathBuilder::new(); base_path.move_to((inset, base_y)); base_path.line_to((w - inset, base_y)); base_path.line_to((w + inset, h)); @@ -280,7 +280,7 @@ impl Mockup { let mut base_paint = paint_from_hex(self.theme.bezel_color()); base_paint.set_style(PaintStyle::Fill); base_paint.set_anti_alias(true); - canvas.draw_path(&base_path, &base_paint); + canvas.draw_path(&base_path.detach(), &base_paint); Ok(()) } diff --git a/crates/rustmotion-components/src/particle.rs b/crates/rustmotion-components/src/particle.rs index c845846..535e7a9 100644 --- a/crates/rustmotion-components/src/particle.rs +++ b/crates/rustmotion-components/src/particle.rs @@ -195,7 +195,7 @@ impl Painter for Particle { paint.set_alpha_f(twinkle); let s = size / 2.0; - let mut path = skia_safe::Path::new(); + let mut path = skia_safe::PathBuilder::new(); path.move_to((x, y - s)); path.line_to((x + s * 0.3, y - s * 0.3)); path.line_to((x + s, y)); @@ -205,7 +205,7 @@ impl Painter for Particle { path.line_to((x - s, y)); path.line_to((x - s * 0.3, y - s * 0.3)); path.close(); - canvas.draw_path(&path, &paint); + canvas.draw_path(&path.detach(), &paint); } ParticleType::Bubbles => { let rise_speed = 50.0 * speed_var; diff --git a/crates/rustmotion-components/src/pointer.rs b/crates/rustmotion-components/src/pointer.rs index 9d0b913..1b1fb19 100644 --- a/crates/rustmotion-components/src/pointer.rs +++ b/crates/rustmotion-components/src/pointer.rs @@ -11,7 +11,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, Paint, PaintStyle, Path}; +use skia_safe::{Canvas, Paint, PaintStyle, Path, PathBuilder}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -172,7 +172,7 @@ impl Pointer { (0.32, 0.51), (0.54, 0.51), ]; - let mut path = Path::new(); + let mut path = PathBuilder::new(); for (i, (x, y)) in OUTLINE.iter().enumerate() { let p = (x * size, y * size); if i == 0 { @@ -182,7 +182,7 @@ impl Pointer { } } path.close(); - path + path.detach() } } diff --git a/crates/rustmotion-components/src/rating.rs b/crates/rustmotion-components/src/rating.rs index 85af5c7..98efd5a 100644 --- a/crates/rustmotion-components/src/rating.rs +++ b/crates/rustmotion-components/src/rating.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, PaintStyle, Path}; +use skia_safe::{Canvas, PaintStyle, Path, PathBuilder}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -81,7 +81,7 @@ impl Rating { fn star_path(cx: f32, cy: f32, outer_radius: f32) -> Path { let inner_radius = outer_radius * 0.4; - let mut path = Path::new(); + let mut path = PathBuilder::new(); for i in 0..10 { let angle = -std::f32::consts::FRAC_PI_2 + i as f32 * std::f32::consts::PI / 5.0; @@ -100,7 +100,7 @@ impl Rating { } } path.close(); - path + path.detach() } } diff --git a/crates/rustmotion-components/src/shape.rs b/crates/rustmotion-components/src/shape.rs index df6ffc1..8844316 100644 --- a/crates/rustmotion-components/src/shape.rs +++ b/crates/rustmotion-components/src/shape.rs @@ -83,31 +83,34 @@ impl Painter for Shape { let dy = (h / 2.0) * rad.sin(); let start = Point::new(cx - dx, cy - dy); let end = Point::new(cx + dx, cy + dy); - skia_safe::shader::Shader::linear_gradient( - (start, end), - skia_safe::gradient_shader::GradientShaderColors::ColorsInSpace( - &colors, - Some(skia_safe::ColorSpace::new_srgb()), - ), + let gradient_colors = skia_safe::gradient::Colors::new( + &colors, stops.as_deref(), skia_safe::TileMode::Clamp, - None, - None, - ) + Some(skia_safe::ColorSpace::new_srgb()), + ); + let g = skia_safe::gradient::Gradient::new( + gradient_colors, + skia_safe::gradient::Interpolation::default(), + ); + skia_safe::gradient::shaders::linear_gradient((start, end), &g, None) } GradientType::Radial => { let center = Point::new(w / 2.0, h / 2.0); let radius = w.max(h) / 2.0; - skia_safe::shader::Shader::radial_gradient( - center, - radius, - skia_safe::gradient_shader::GradientShaderColors::ColorsInSpace( - &colors, - Some(skia_safe::ColorSpace::new_srgb()), - ), + let gradient_colors = skia_safe::gradient::Colors::new( + &colors, stops.as_deref(), skia_safe::TileMode::Clamp, - None, + Some(skia_safe::ColorSpace::new_srgb()), + ); + let g = skia_safe::gradient::Gradient::new( + gradient_colors, + skia_safe::gradient::Interpolation::default(), + ); + skia_safe::gradient::shaders::radial_gradient( + (center, radius), + &g, None, ) } diff --git a/crates/rustmotion-components/src/skeleton.rs b/crates/rustmotion-components/src/skeleton.rs index b9bbe27..0106344 100644 --- a/crates/rustmotion-components/src/skeleton.rs +++ b/crates/rustmotion-components/src/skeleton.rs @@ -1,6 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, Color, PaintStyle, Point, Rect}; +use skia_safe::gradient::{self, Colors, Gradient}; +use skia_safe::{Canvas, Color, Color4f, PaintStyle, Point, Rect}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -105,19 +106,19 @@ impl Skeleton { let transparent = Color::from_argb(0, r, g, b); let highlight = Color::from_argb(180, r, g, b); - let shader = skia_safe::shader::Shader::linear_gradient( + let colors4f = [ + Color4f::from(transparent), + Color4f::from(highlight), + Color4f::from(transparent), + ]; + let stops = Colors::new(&colors4f, None, skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(stops, gradient::Interpolation::default()); + let shader = gradient::shaders::linear_gradient( ( Point::new(shimmer_x, 0.0), Point::new(shimmer_x + shimmer_w, 0.0), ), - skia_safe::gradient_shader::GradientShaderColors::Colors(&[ - transparent, - highlight, - transparent, - ]), - None, - skia_safe::TileMode::Clamp, - None, + &grad, None, ); diff --git a/crates/rustmotion-components/src/sparkline.rs b/crates/rustmotion-components/src/sparkline.rs index e5fde0f..da125f3 100644 --- a/crates/rustmotion-components/src/sparkline.rs +++ b/crates/rustmotion-components/src/sparkline.rs @@ -1,6 +1,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, Color, PaintStyle, Path, Point, Rect}; +use skia_safe::gradient::{self, Colors, Gradient}; +use skia_safe::{Canvas, Color, Color4f, PaintStyle, PathBuilder, Point, Rect}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -116,8 +117,8 @@ impl Sparkline { let pad = self.stroke_width; - let mut line_path = Path::new(); - let mut fill_path = Path::new(); + let mut line_path = PathBuilder::new(); + let mut fill_path = PathBuilder::new(); for (i, &val) in self.data.iter().enumerate() { let x = pad + (i as f32 / (n - 1) as f32) * (w - pad * 2.0); @@ -152,15 +153,12 @@ impl Sparkline { let top_color = Color::from_argb((self.fill_opacity * 255.0) as u8, r, g, b); let bottom_color = Color::from_argb(0, r, g, b); - let shader = skia_safe::shader::Shader::linear_gradient( + let colors4f = [Color4f::from(top_color), Color4f::from(bottom_color)]; + let stops = Colors::new(&colors4f, None, skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(stops, gradient::Interpolation::default()); + let shader = gradient::shaders::linear_gradient( (Point::new(0.0, 0.0), Point::new(0.0, h)), - skia_safe::gradient_shader::GradientShaderColors::Colors(&[ - top_color, - bottom_color, - ]), - None, - skia_safe::TileMode::Clamp, - None, + &grad, None, ); @@ -169,7 +167,7 @@ impl Sparkline { fill_paint.set_style(PaintStyle::Fill); fill_paint.set_anti_alias(true); fill_paint.set_shader(shader); - canvas.draw_path(&fill_path, &fill_paint); + canvas.draw_path(&fill_path.detach(), &fill_paint); } } @@ -180,7 +178,7 @@ impl Sparkline { line_paint.set_anti_alias(true); line_paint.set_stroke_cap(skia_safe::paint::Cap::Round); line_paint.set_stroke_join(skia_safe::paint::Join::Round); - canvas.draw_path(&line_path, &line_paint); + canvas.draw_path(&line_path.detach(), &line_paint); canvas.restore(); } diff --git a/crates/rustmotion-components/src/stat.rs b/crates/rustmotion-components/src/stat.rs index 740ed5d..fe181b1 100644 --- a/crates/rustmotion-components/src/stat.rs +++ b/crates/rustmotion-components/src/stat.rs @@ -1,6 +1,9 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, Color, ColorType, ImageInfo, Paint, PaintStyle, Path, Point, Rect}; +use skia_safe::gradient::{self, Colors, Gradient}; +use skia_safe::{ + Canvas, Color, Color4f, ColorType, ImageInfo, Paint, PaintStyle, PathBuilder, Point, Rect, +}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -333,8 +336,8 @@ impl Stat { let spark_color = self.sparkline_color.as_deref().unwrap_or("#3B82F6"); - let mut line_path = Path::new(); - let mut fill_path = Path::new(); + let mut line_path = PathBuilder::new(); + let mut fill_path = PathBuilder::new(); for (i, &val) in self.sparkline_data.iter().enumerate() { let x = pad + (i as f32 / (n - 1) as f32) * spark_w; @@ -357,15 +360,12 @@ impl Stat { let (r, g, b, _) = parse_hex_color(spark_color); let top_color = Color::from_argb(50, r, g, b); let bottom_color = Color::from_argb(0, r, g, b); - let shader = skia_safe::shader::Shader::linear_gradient( + let colors4f = [Color4f::from(top_color), Color4f::from(bottom_color)]; + let stops = Colors::new(&colors4f, None, skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(stops, gradient::Interpolation::default()); + let shader = gradient::shaders::linear_gradient( (Point::new(0.0, spark_y), Point::new(0.0, spark_y + spark_h)), - skia_safe::gradient_shader::GradientShaderColors::Colors(&[ - top_color, - bottom_color, - ]), - None, - skia_safe::TileMode::Clamp, - None, + &grad, None, ); if let Some(shader) = shader { @@ -373,7 +373,7 @@ impl Stat { fp.set_style(PaintStyle::Fill); fp.set_anti_alias(true); fp.set_shader(shader); - canvas.draw_path(&fill_path, &fp); + canvas.draw_path(&fill_path.detach(), &fp); } let mut line_paint = paint_from_hex(spark_color); @@ -382,7 +382,7 @@ impl Stat { line_paint.set_anti_alias(true); line_paint.set_stroke_cap(skia_safe::paint::Cap::Round); line_paint.set_stroke_join(skia_safe::paint::Join::Round); - canvas.draw_path(&line_path, &line_paint); + canvas.draw_path(&line_path.detach(), &line_paint); } canvas.restore(); diff --git a/crates/rustmotion-components/src/success_check.rs b/crates/rustmotion-components/src/success_check.rs index 255792e..239118a 100644 --- a/crates/rustmotion-components/src/success_check.rs +++ b/crates/rustmotion-components/src/success_check.rs @@ -8,7 +8,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, PaintStyle, Path}; +use skia_safe::{Canvas, PaintStyle, Path, PathBuilder}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::{ease, AnimatedProperties}; @@ -123,11 +123,11 @@ impl SuccessCheck { /// The checkmark itself, in units of `size`. pub(crate) fn check_path(size: f32) -> Path { - let mut path = Path::new(); + let mut path = PathBuilder::new(); path.move_to((0.28 * size, 0.52 * size)); path.line_to((0.44 * size, 0.69 * size)); path.line_to((0.73 * size, 0.33 * size)); - path + path.detach() } } diff --git a/crates/rustmotion-components/src/svg.rs b/crates/rustmotion-components/src/svg.rs index 824e6e6..ccf9e24 100644 --- a/crates/rustmotion-components/src/svg.rs +++ b/crates/rustmotion-components/src/svg.rs @@ -1,6 +1,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, ColorType, ImageInfo, Matrix, Paint, PaintStyle, Path, PathMeasure, Rect}; +use skia_safe::{ + Canvas, ColorType, ImageInfo, Matrix, Paint, PaintStyle, Path, PathBuilder, PathMeasure, Rect, +}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -60,7 +62,7 @@ fn tiny_path_to_skia(tsp: &tiny_skia::Path, abs_transform: tiny_skia::Transform) let t = abs_transform; let matrix = Matrix::new_all(t.sx, t.kx, t.tx, t.ky, t.sy, t.ty, 0.0, 0.0, 1.0); - let mut skia_path = Path::new(); + let mut skia_path = PathBuilder::new(); for segment in tsp.segments() { match segment { tiny_skia::PathSegment::MoveTo(p) => { @@ -87,7 +89,7 @@ fn tiny_path_to_skia(tsp: &tiny_skia::Path, abs_transform: tiny_skia::Transform) } } } - skia_path + skia_path.detach() } /// Recursively collect (skia_path, skia_color, stroke_width) for each visible diff --git a/crates/rustmotion-components/src/tooltip.rs b/crates/rustmotion-components/src/tooltip.rs index 61299b5..44cd1a5 100644 --- a/crates/rustmotion-components/src/tooltip.rs +++ b/crates/rustmotion-components/src/tooltip.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, PaintStyle, Path, Rect}; +use skia_safe::{Canvas, PaintStyle, PathBuilder, Rect}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -133,7 +133,7 @@ impl Tooltip { // Arrow triangle if !matches!(self.arrow, TooltipArrow::None) { - let mut arrow_path = Path::new(); + let mut arrow_path = PathBuilder::new(); match self.arrow { TooltipArrow::Bottom => { let cx = body_x + body_w / 2.0; @@ -169,7 +169,7 @@ impl Tooltip { } TooltipArrow::None => {} } - canvas.draw_path(&arrow_path, &bg_paint); + canvas.draw_path(&arrow_path.detach(), &bg_paint); } // Text centered in body diff --git a/crates/rustmotion-components/src/waveform.rs b/crates/rustmotion-components/src/waveform.rs index 8f7e7df..07ff6bd 100644 --- a/crates/rustmotion-components/src/waveform.rs +++ b/crates/rustmotion-components/src/waveform.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, Color, Paint, PaintStyle, Path}; +use skia_safe::{Canvas, Color, Paint, PaintStyle, PathBuilder}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -116,12 +116,12 @@ impl Painter for Waveform { if points.len() < 2 { return; } - let mut path = Path::new(); + let mut path = PathBuilder::new(); path.move_to((points[0].0, points[0].1)); for &(x, y) in &points[1..] { path.line_to((x, y)); } - canvas.draw_path(&path, &paint); + canvas.draw_path(&path.detach(), &paint); } DrawStyle::Filled => { // Filled area @@ -132,26 +132,26 @@ impl Painter for Waveform { if points.is_empty() { return; } - let mut fill_path = Path::new(); + let mut fill_path = PathBuilder::new(); fill_path.move_to((0.0, h / 2.0)); for &(x, y) in &points { fill_path.line_to((x, y)); } fill_path.line_to((w, h / 2.0)); fill_path.close(); - canvas.draw_path(&fill_path, &paint); + canvas.draw_path(&fill_path.detach(), &paint); // Outline paint.set_style(PaintStyle::Stroke); paint.set_stroke_width(1.5); paint.set_color(color); if points.len() >= 2 { - let mut outline = Path::new(); + let mut outline = PathBuilder::new(); outline.move_to((points[0].0, points[0].1)); for &(x, y) in &points[1..] { outline.line_to((x, y)); } - canvas.draw_path(&outline, &paint); + canvas.draw_path(&outline.detach(), &paint); } } } diff --git a/crates/rustmotion-core/Cargo.toml b/crates/rustmotion-core/Cargo.toml index 34e984d..4999d58 100644 --- a/crates/rustmotion-core/Cargo.toml +++ b/crates/rustmotion-core/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/LeadcodeDev/rustmotion" serde = { version = "1", features = ["derive"] } serde_json = "1" schemars = "0.8" -skia-safe = "0.82" +skia-safe = "0.99" resvg = "0.44" usvg = "0.44" tiny-skia = "0.11" diff --git a/crates/rustmotion-core/src/engine/animator.rs b/crates/rustmotion-core/src/engine/animator.rs index 89e9331..edcaedf 100644 --- a/crates/rustmotion-core/src/engine/animator.rs +++ b/crates/rustmotion-core/src/engine/animator.rs @@ -1323,7 +1323,7 @@ fn motion_path_sample(cfg: &MotionPathConfig, time: f64) -> MotionPathSample { // to intuit, and it skips constructing/querying the measure entirely // for the single most common degenerate input (a single-point path). if length <= MOTION_PATH_MIN_LENGTH { - let (x, y) = path.get_point(0).map_or((0.0, 0.0), |p| (p.x, p.y)); + let (x, y) = path.points().first().map_or((0.0, 0.0), |p| (p.x, p.y)); return MotionPathSample { dx: x, dy: y, @@ -1352,7 +1352,7 @@ fn motion_path_sample(cfg: &MotionPathConfig, time: f64) -> MotionPathSample { // path's start rather than let a missing sample surface as a jump // to the component's untranslated origin or a NaN. None => { - let (x, y) = path.get_point(0).map_or((0.0, 0.0), |p| (p.x, p.y)); + let (x, y) = path.points().first().map_or((0.0, 0.0), |p| (p.x, p.y)); MotionPathSample { dx: x, dy: y, diff --git a/crates/rustmotion-core/src/engine/paint_pass.rs b/crates/rustmotion-core/src/engine/paint_pass.rs index 0255ce4..496cdea 100644 --- a/crates/rustmotion-core/src/engine/paint_pass.rs +++ b/crates/rustmotion-core/src/engine/paint_pass.rs @@ -17,9 +17,10 @@ use std::cell::RefCell; +use skia_safe::gradient::{self, Colors as GradientColors, Gradient}; use skia_safe::{ - canvas::SaveLayerRec, Canvas, ClipOp, Color as SColor, Color4f, Paint, PaintStyle, Path, Point, - RRect, Rect, M44, V3, + canvas::SaveLayerRec, Canvas, ClipOp, Color as SColor, Color4f, Paint, PaintStyle, PathBuilder, + Point, RRect, Rect, M44, V3, }; use crate::css::style::{ @@ -572,18 +573,14 @@ fn paint_shimmer_band( let p0 = Point::new(cx + dx * (centre - band), cy + dy * (centre - band)); let p1 = Point::new(cx + dx * (centre + band), cy + dy * (centre + band)); - let Some(shader) = skia_safe::shader::Shader::linear_gradient( - (p0, p1), - skia_safe::gradient_shader::GradientShaderColors::Colors(&[ - transparent, - highlight, - transparent, - ]), - None, - skia_safe::TileMode::Clamp, - None, - None, - ) else { + let colors4f = [ + Color4f::from(transparent), + Color4f::from(highlight), + Color4f::from(transparent), + ]; + let gradient_colors = GradientColors::new(&colors4f, None, skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(gradient_colors, gradient::Interpolation::default()); + let Some(shader) = gradient::shaders::linear_gradient((p0, p1), &grad, None) else { return; }; @@ -1195,14 +1192,10 @@ fn paint_bg_layer(canvas: &Canvas, rrect: &RRect, layer: &BackgroundLayer) { let bounds = rrect.bounds(); let (p0, p1) = gradient_endpoints(*bounds, angle.unwrap_or(180.0)); let (colors, positions) = gradient_stops(stops); - if let Some(shader) = skia_safe::gradient_shader::linear( - (p0, p1), - colors.as_slice(), - positions.as_slice(), - skia_safe::TileMode::Clamp, - None, - None, - ) { + let gradient_colors = + GradientColors::new(&colors, Some(&positions), skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(gradient_colors, gradient::Interpolation::default()); + if let Some(shader) = gradient::shaders::linear_gradient((p0, p1), &grad, None) { paint.set_shader(shader); canvas.draw_rrect(rrect, &paint); } @@ -1215,15 +1208,11 @@ fn paint_bg_layer(canvas: &Canvas, rrect: &RRect, layer: &BackgroundLayer) { ); let radius = bounds.width().max(bounds.height()) / 2.0; let (colors, positions) = gradient_stops(stops); - if let Some(shader) = skia_safe::gradient_shader::radial( - center, - radius, - colors.as_slice(), - positions.as_slice(), - skia_safe::TileMode::Clamp, - None, - None, - ) { + let gradient_colors = + GradientColors::new(&colors, Some(&positions), skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(gradient_colors, gradient::Interpolation::default()); + if let Some(shader) = gradient::shaders::radial_gradient((center, radius), &grad, None) + { paint.set_shader(shader); canvas.draw_rrect(rrect, &paint); } @@ -1236,15 +1225,14 @@ fn paint_bg_layer(canvas: &Canvas, rrect: &RRect, layer: &BackgroundLayer) { bounds.top + bounds.height() / 2.0, ); let (colors, positions) = gradient_stops(stops); - if let Some(shader) = skia_safe::gradient_shader::sweep( - center, - colors.as_slice(), - positions.as_slice(), - skia_safe::TileMode::Clamp, - None, - None, - None, - ) { + let gradient_colors = + GradientColors::new(&colors, Some(&positions), skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(gradient_colors, gradient::Interpolation::default()); + // `None` angles on the deprecated API defaulted to a full 0..360 + // sweep; the new signature makes that range mandatory. + if let Some(shader) = + gradient::shaders::sweep_gradient(center, (0.0, 360.0), &grad, None) + { paint.set_shader(shader); canvas.draw_rrect(rrect, &paint); } @@ -1255,12 +1243,12 @@ fn paint_bg_layer(canvas: &Canvas, rrect: &RRect, layer: &BackgroundLayer) { } } -fn gradient_stops(stops: &[crate::css::style::GradientStop]) -> (Vec, Vec) { +fn gradient_stops(stops: &[crate::css::style::GradientStop]) -> (Vec, Vec) { let mut colors = Vec::with_capacity(stops.len()); let mut positions = Vec::with_capacity(stops.len()); let n = stops.len().max(1); for (i, s) in stops.iter().enumerate() { - colors.push(parse_color(&s.color)); + colors.push(Color4f::from(parse_color(&s.color))); let default_offset = i as f32 / (n.saturating_sub(1).max(1) as f32); positions.push(s.offset.unwrap_or(default_offset)); } @@ -1373,10 +1361,10 @@ fn paint_gradient_border( ]; let inner = rrect_from_corners(inner_rect, inner_radius); - let colors: Vec = gb + let colors: Vec = gb .colors .iter() - .map(|c| parse_color_string(c).unwrap_or_else(|| unresolved_color(c))) + .map(|c| Color4f::from(parse_color_string(c).unwrap_or_else(|| unresolved_color(c)))) .collect(); let n = colors.len(); let positions: Vec = (0..n) @@ -1385,14 +1373,10 @@ fn paint_gradient_border( let bounds = outer.bounds(); let (p0, p1) = gradient_endpoints(*bounds, gb.angle); - let Some(shader) = skia_safe::gradient_shader::linear( - (p0, p1), - colors.as_slice(), - positions.as_slice(), - skia_safe::TileMode::Clamp, - None, - None, - ) else { + let gradient_colors = + GradientColors::new(&colors, Some(&positions), skia_safe::TileMode::Clamp, None); + let grad = Gradient::new(gradient_colors, gradient::Interpolation::default()); + let Some(shader) = gradient::shaders::linear_gradient((p0, p1), &grad, None) else { return; }; @@ -1533,11 +1517,11 @@ fn paint_box_shadow( } } // Cheap approximation — TODO: proper inset shadow with subtraction path. - let mut path = Path::new(); - path.add_rrect(outer, None); - path.add_rrect(inner, None); + let mut path = PathBuilder::new(); + path.add_rrect(outer, None, None); + path.add_rrect(inner, None, None); path.set_fill_type(skia_safe::PathFillType::EvenOdd); - canvas.draw_path(&path, &clear); + canvas.draw_path(&path.detach(), &clear); canvas.restore(); } } diff --git a/crates/rustmotion-core/src/engine/renderer/shapes.rs b/crates/rustmotion-core/src/engine/renderer/shapes.rs index 895f984..48156aa 100644 --- a/crates/rustmotion-core/src/engine/renderer/shapes.rs +++ b/crates/rustmotion-core/src/engine/renderer/shapes.rs @@ -13,35 +13,35 @@ pub fn build_shape_path( ) -> Option { match shape_type { ShapeType::Rect => { - let mut path = skia_safe::Path::new(); - path.add_rect(Rect::from_xywh(x, y, w, h), None); - Some(path) + let mut path = skia_safe::PathBuilder::new(); + path.add_rect(Rect::from_xywh(x, y, w, h), None, None); + Some(path.detach()) } ShapeType::RoundedRect => { let r = corner_radius.unwrap_or(8.0); let rrect = skia_safe::RRect::new_rect_xy(Rect::from_xywh(x, y, w, h), r, r); - let mut path = skia_safe::Path::new(); - path.add_rrect(rrect, None); - Some(path) + let mut path = skia_safe::PathBuilder::new(); + path.add_rrect(rrect, None, None); + Some(path.detach()) } ShapeType::Circle => { let radius = w.min(h) / 2.0; - let mut path = skia_safe::Path::new(); + let mut path = skia_safe::PathBuilder::new(); path.add_circle((x + w / 2.0, y + h / 2.0), radius, None); - Some(path) + Some(path.detach()) } ShapeType::Ellipse => { - let mut path = skia_safe::Path::new(); - path.add_oval(Rect::from_xywh(x, y, w, h), None); - Some(path) + let mut path = skia_safe::PathBuilder::new(); + path.add_oval(Rect::from_xywh(x, y, w, h), None, None); + Some(path.detach()) } ShapeType::Triangle => { - let mut path = skia_safe::Path::new(); + let mut path = skia_safe::PathBuilder::new(); path.move_to((x + w / 2.0, y)); path.line_to((x + w, y + h)); path.line_to((x, y + h)); path.close(); - Some(path) + Some(path.detach()) } ShapeType::Star { points } => { let cx = x + w / 2.0; @@ -49,7 +49,7 @@ pub fn build_shape_path( let outer_r = w.min(h) / 2.0; let inner_r = outer_r * 0.4; let n = *points as usize; - let mut path = skia_safe::Path::new(); + let mut path = skia_safe::PathBuilder::new(); for i in 0..(n * 2) { let angle = (i as f32) * std::f32::consts::PI / n as f32 - std::f32::consts::FRAC_PI_2; @@ -63,14 +63,14 @@ pub fn build_shape_path( } } path.close(); - Some(path) + Some(path.detach()) } ShapeType::Polygon { sides } => { let cx = x + w / 2.0; let cy = y + h / 2.0; let r = w.min(h) / 2.0; let n = *sides as usize; - let mut path = skia_safe::Path::new(); + let mut path = skia_safe::PathBuilder::new(); for i in 0..n { let angle = (i as f32) * 2.0 * std::f32::consts::PI / n as f32 - std::f32::consts::FRAC_PI_2; @@ -83,7 +83,7 @@ pub fn build_shape_path( } } path.close(); - Some(path) + Some(path.detach()) } ShapeType::Path { data } => skia_safe::Path::from_svg(data), } @@ -117,12 +117,12 @@ pub fn draw_shape_path( canvas.draw_oval(rect, paint); } ShapeType::Triangle => { - let mut path = skia_safe::Path::new(); + let mut path = skia_safe::PathBuilder::new(); path.move_to((x + w / 2.0, y)); path.line_to((x + w, y + h)); path.line_to((x, y + h)); path.close(); - canvas.draw_path(&path, paint); + canvas.draw_path(&path.detach(), paint); } ShapeType::Star { points } => { let cx = x + w / 2.0; @@ -130,7 +130,7 @@ pub fn draw_shape_path( let outer_r = w.min(h) / 2.0; let inner_r = outer_r * 0.4; let n = *points as usize; - let mut path = skia_safe::Path::new(); + let mut path = skia_safe::PathBuilder::new(); for i in 0..(n * 2) { let angle = (i as f32) * std::f32::consts::PI / n as f32 - std::f32::consts::FRAC_PI_2; @@ -144,14 +144,14 @@ pub fn draw_shape_path( } } path.close(); - canvas.draw_path(&path, paint); + canvas.draw_path(&path.detach(), paint); } ShapeType::Polygon { sides } => { let cx = x + w / 2.0; let cy = y + h / 2.0; let r = w.min(h) / 2.0; let n = *sides as usize; - let mut path = skia_safe::Path::new(); + let mut path = skia_safe::PathBuilder::new(); for i in 0..n { let angle = (i as f32) * 2.0 * std::f32::consts::PI / n as f32 - std::f32::consts::FRAC_PI_2; @@ -164,7 +164,7 @@ pub fn draw_shape_path( } } path.close(); - canvas.draw_path(&path, paint); + canvas.draw_path(&path.detach(), paint); } ShapeType::Path { data } => { if let Some(path) = skia_safe::Path::from_svg(data) { diff --git a/crates/rustmotion-core/src/engine/transition.rs b/crates/rustmotion-core/src/engine/transition.rs index da1859a..b256a08 100644 --- a/crates/rustmotion-core/src/engine/transition.rs +++ b/crates/rustmotion-core/src/engine/transition.rs @@ -3,7 +3,7 @@ use crate::schema::{ EasingType, PanBackground, PixelDissolveOrder, Transition, TransitionCorner, TransitionDirection, TransitionType, }; -use skia_safe::{surfaces, Color4f, ColorType, ImageInfo, Paint, Path, Rect}; +use skia_safe::{surfaces, Color4f, ColorType, ImageInfo, Paint, PathBuilder, Rect}; /// The per-type knobs a transition may read, bundled. /// @@ -522,7 +522,7 @@ fn clock_wipe(frame_a: &[u8], frame_b: &[u8], width: u32, height: u32, progress: let sweep_angle = progress * 360.0; let start_angle = -90.0; // Start from top - let mut path = Path::new(); + let mut path = PathBuilder::new(); path.move_to((cx, cy)); path.arc_to( Rect::from_xywh(cx - radius, cy - radius, radius * 2.0, radius * 2.0), @@ -533,7 +533,7 @@ fn clock_wipe(frame_a: &[u8], frame_b: &[u8], width: u32, height: u32, progress: path.close(); canvas.save(); - canvas.clip_path(&path, skia_safe::ClipOp::Intersect, true); + canvas.clip_path(&path.detach(), skia_safe::ClipOp::Intersect, true); canvas.draw_image(&img_b, (0.0, 0.0), None); canvas.restore(); @@ -572,11 +572,11 @@ fn iris_transition( canvas.draw_image(&img_a, (0.0, 0.0), None); // Clip frame B to an expanding circle - let mut path = Path::new(); + let mut path = PathBuilder::new(); path.add_circle((cx, cy), radius, None); canvas.save(); - canvas.clip_path(&path, skia_safe::ClipOp::Intersect, true); + canvas.clip_path(&path.detach(), skia_safe::ClipOp::Intersect, true); canvas.draw_image(&img_b, (0.0, 0.0), None); canvas.restore(); diff --git a/crates/rustmotion/Cargo.toml b/crates/rustmotion/Cargo.toml index fad36df..00ff312 100644 --- a/crates/rustmotion/Cargo.toml +++ b/crates/rustmotion/Cargo.toml @@ -13,7 +13,7 @@ path = "src/lib.rs" rustmotion-core.workspace = true rustmotion-components = { workspace = true, features = ["lottie-native"] } rustmotion-html.workspace = true -skia-safe = "0.82" +skia-safe = "0.99" serde = { version = "1", features = ["derive"] } serde_json = "1" openh264 = "0.6" diff --git a/crates/rustmotion/src/engine/render/background.rs b/crates/rustmotion/src/engine/render/background.rs index a50db3f..f7a27bb 100644 --- a/crates/rustmotion/src/engine/render/background.rs +++ b/crates/rustmotion/src/engine/render/background.rs @@ -112,7 +112,10 @@ fn draw_bg_gradient_shift( width: f32, height: f32, ) { - use skia_safe::{gradient_shader::GradientShaderColors, Point}; + use skia_safe::{ + gradient::{self, Colors, Gradient}, + Point, + }; if cfg.colors.len() < 2 { return; @@ -143,27 +146,26 @@ fn draw_bg_gradient_shift( let half_diag = (width.powi(2) + height.powi(2)).sqrt() / 2.0; let start = Point::new(cx - rad.cos() * half_diag, cy - rad.sin() * half_diag); let end = Point::new(cx + rad.cos() * half_diag, cy + rad.sin() * half_diag); - skia_safe::shader::Shader::linear_gradient( - (start, end), - GradientShaderColors::ColorsInSpace(&colors, None), + let gradient_colors = Colors::new( + &colors, Some(&positions[..]), skia_safe::TileMode::Clamp, None, - None, - ) + ); + let gradient = Gradient::new(gradient_colors, gradient::Interpolation::default()); + gradient::shaders::linear_gradient((start, end), &gradient, None) } GradientType::Radial => { let center = Point::new(width / 2.0, height / 2.0); let radius = width.max(height) / 2.0; - skia_safe::shader::Shader::radial_gradient( - center, - radius, - GradientShaderColors::ColorsInSpace(&colors, None), + let gradient_colors = Colors::new( + &colors, Some(&positions[..]), skia_safe::TileMode::Clamp, None, - None, - ) + ); + let gradient = Gradient::new(gradient_colors, gradient::Interpolation::default()); + gradient::shaders::radial_gradient((center, radius), &gradient, None) } };