From 5f7b25851a1912078265704eee8d63c7a025e778 Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Thu, 20 Aug 2026 10:04:44 +0200 Subject: [PATCH 1/2] fix(skia): migrate to skia-safe 0.99 so docs.rs can build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit skia-bindings 0.82.0 has a broken docs.rs path: it detects the build, announces it is copying bindings_docs.rs into OUT_DIR, and the copy fails with NotFound. The build script exits 1 and rustdoc never runs, which is why 0.6.1 published with no documentation. skia-safe 0.82.0 has no docs there either, for the same reason; 0.99.0 does. The only breaking change that reaches us is Path becoming immutable: Skia moved construction into SkPathBuilder, so the mutating methods left Path. Of the 99 files that touch skia_safe, 22 needed work — Canvas, Paint, Surface, ImageInfo, FontStyle, Shader and Data crossed seventeen minor versions unchanged. Three details worth keeping: - add_rect, add_oval and add_rrect gained a third argument (start_index); add_circle already had three and is untouched. - Path::from_svg still returns a real Path. A blanket rewrite of draw_path(&path adds a .detach() there that does not compile — and would have been worse if it had. - chart/radar.rs draws one path twice, filled then stroked. That is the single place snapshot() is correct and detach() would empty the builder too early. get_point is replaced by points().first(), same semantics. The 25 remaining deprecations are all on the gradient shader API and are held under narrowly scoped allow(deprecated) rather than migrated: the replacement drops TileMode from the signature, so moving it blind changes rendering in ways that look plausible. Tracked in #215. Closes #214 --- Cargo.lock | 85 +++++++++++++++---- crates/rustmotion-components/Cargo.toml | 2 +- crates/rustmotion-components/src/arrow.rs | 10 +-- crates/rustmotion-components/src/callout.rs | 6 +- .../rustmotion-components/src/chart/funnel.rs | 10 +-- .../rustmotion-components/src/chart/line.rs | 22 +++-- crates/rustmotion-components/src/chart/pie.rs | 6 +- .../rustmotion-components/src/chart/radar.rs | 12 +-- crates/rustmotion-components/src/connector.rs | 10 +-- crates/rustmotion-components/src/divider.rs | 6 +- .../src/gradient_text.rs | 4 + crates/rustmotion-components/src/mockup.rs | 6 +- crates/rustmotion-components/src/particle.rs | 4 +- crates/rustmotion-components/src/pointer.rs | 6 +- crates/rustmotion-components/src/rating.rs | 6 +- crates/rustmotion-components/src/shape.rs | 4 + crates/rustmotion-components/src/skeleton.rs | 4 + crates/rustmotion-components/src/sparkline.rs | 14 +-- crates/rustmotion-components/src/stat.rs | 14 +-- .../src/success_check.rs | 6 +- crates/rustmotion-components/src/svg.rs | 8 +- crates/rustmotion-components/src/tooltip.rs | 6 +- crates/rustmotion-components/src/waveform.rs | 14 +-- crates/rustmotion-core/Cargo.toml | 2 +- crates/rustmotion-core/src/engine/animator.rs | 4 +- .../rustmotion-core/src/engine/paint_pass.rs | 24 ++++-- .../src/engine/renderer/shapes.rs | 46 +++++----- .../rustmotion-core/src/engine/transition.rs | 10 +-- crates/rustmotion/Cargo.toml | 2 +- .../src/engine/render/background.rs | 4 + 30 files changed, 224 insertions(+), 133 deletions(-) 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..b8b3ce4 100644 --- a/crates/rustmotion-components/src/chart/line.rs +++ b/crates/rustmotion-components/src/chart/line.rs @@ -1,5 +1,5 @@ use rustmotion_core::error::Result; -use skia_safe::{Canvas, Color, PaintStyle, Path, Point, Rect}; +use skia_safe::{Canvas, Color, PaintStyle, PathBuilder, Point, Rect}; use rustmotion_core::engine::renderer::{paint_from_hex, parse_hex_color}; @@ -67,8 +67,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 +102,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() { @@ -127,6 +127,10 @@ impl Chart { Ok(()) } + // The non-deprecated gradient API moves TileMode out of the signature; + // the transposition is tracked in #215, and doing it here without pixel + // tests would change rendering silently. + #[allow(deprecated)] pub(super) fn render_area(&self, canvas: &Canvas, w: f32, h: f32, progress: f32) -> Result<()> { let (mt, mr, mb, ml) = self.chart_margins(); let chart_w = w - ml - mr; @@ -168,8 +172,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 @@ -241,7 +245,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 +253,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..a3d210a 100644 --- a/crates/rustmotion-components/src/gradient_text.rs +++ b/crates/rustmotion-components/src/gradient_text.rs @@ -83,6 +83,10 @@ impl GradientText { } impl GradientText { + // The non-deprecated gradient API moves TileMode out of the signature; + // the transposition is tracked in #215, and doing it here without pixel + // tests would change rendering silently. + #[allow(deprecated)] fn paint( &self, canvas: &Canvas, 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..c193e34 100644 --- a/crates/rustmotion-components/src/shape.rs +++ b/crates/rustmotion-components/src/shape.rs @@ -41,6 +41,10 @@ rustmotion_core::impl_traits!(Shape { }); impl Painter for Shape { + // The non-deprecated gradient API moves TileMode out of the signature; + // the transposition is tracked in #215, and doing it here without pixel + // tests would change rendering silently. + #[allow(deprecated)] fn paint_content( &self, canvas: &Canvas, diff --git a/crates/rustmotion-components/src/skeleton.rs b/crates/rustmotion-components/src/skeleton.rs index b9bbe27..0bbbfbf 100644 --- a/crates/rustmotion-components/src/skeleton.rs +++ b/crates/rustmotion-components/src/skeleton.rs @@ -85,6 +85,10 @@ rustmotion_core::impl_traits!(Skeleton { }); impl Skeleton { + // The non-deprecated gradient API moves TileMode out of the signature; + // the transposition is tracked in #215, and doing it here without pixel + // tests would change rendering silently. + #[allow(deprecated)] fn draw_shimmer_rect(&self, canvas: &Canvas, rect: Rect, radius: f32, time: f64) { let w = rect.width(); diff --git a/crates/rustmotion-components/src/sparkline.rs b/crates/rustmotion-components/src/sparkline.rs index e5fde0f..11794b0 100644 --- a/crates/rustmotion-components/src/sparkline.rs +++ b/crates/rustmotion-components/src/sparkline.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, Color, PaintStyle, Path, Point, Rect}; +use skia_safe::{Canvas, Color, PaintStyle, PathBuilder, Point, Rect}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -102,6 +102,10 @@ impl Sparkline { 1.0 - (1.0 - p).powi(3) } + // The non-deprecated gradient API moves TileMode out of the signature; + // the transposition is tracked in #215, and doing it here without pixel + // tests would change rendering silently. + #[allow(deprecated)] fn paint(&self, canvas: &Canvas, layout_w: f32, layout_h: f32, time: f64) { let w = layout_w; let h = layout_h; @@ -116,8 +120,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); @@ -169,7 +173,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 +184,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..d8b7123 100644 --- a/crates/rustmotion-components/src/stat.rs +++ b/crates/rustmotion-components/src/stat.rs @@ -1,6 +1,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use skia_safe::{Canvas, Color, ColorType, ImageInfo, Paint, PaintStyle, Path, Point, Rect}; +use skia_safe::{Canvas, Color, ColorType, ImageInfo, Paint, PaintStyle, PathBuilder, Point, Rect}; use rustmotion_core::css::CssStyle; use rustmotion_core::engine::animator::AnimatedProperties; @@ -83,6 +83,10 @@ rustmotion_core::impl_traits!(Stat { }); impl Stat { + // The non-deprecated gradient API moves TileMode out of the signature; + // the transposition is tracked in #215, and doing it here without pixel + // tests would change rendering silently. + #[allow(deprecated)] fn paint(&self, canvas: &Canvas, layout_w: f32, layout_h: f32) { let w = layout_w; let h = layout_h; @@ -333,8 +337,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; @@ -373,7 +377,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 +386,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..bbe0a3c 100644 --- a/crates/rustmotion-core/src/engine/paint_pass.rs +++ b/crates/rustmotion-core/src/engine/paint_pass.rs @@ -18,8 +18,8 @@ use std::cell::RefCell; 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::{ @@ -540,6 +540,10 @@ fn active_shimmer(css: &CssStyle, time: f64) -> Option<(&crate::schema::ShimmerC /// Stamp the sweeping band onto the layer built by steps 9-10, restricted to /// the pixels that layer actually painted. +// The non-deprecated gradient API moves TileMode out of the signature; the +// transposition is tracked in #215, and doing it here without pixel tests +// would change rendering silently. +#[allow(deprecated)] fn paint_shimmer_band( canvas: &Canvas, layout: &BoxLayout, @@ -1183,6 +1187,10 @@ fn paint_background( } } +// The non-deprecated gradient API moves TileMode out of the signature; the +// transposition is tracked in #215, and doing it here without pixel tests +// would change rendering silently. +#[allow(deprecated)] fn paint_bg_layer(canvas: &Canvas, rrect: &RRect, layer: &BackgroundLayer) { let mut paint = Paint::default(); paint.set_anti_alias(true); @@ -1339,6 +1347,10 @@ fn paint_border( /// The gradient is linear along `gb.angle` with the **same angle convention as /// `background` linear gradients** (see [`gradient_endpoints`]) so the two /// stay visually consistent within one style block. Colors are evenly spaced. +// The non-deprecated gradient API moves TileMode out of the signature; the +// transposition is tracked in #215, and doing it here without pixel tests +// would change rendering silently. +#[allow(deprecated)] fn paint_gradient_border( canvas: &Canvas, layout: &BoxLayout, @@ -1533,11 +1545,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..cfc42fc 100644 --- a/crates/rustmotion/src/engine/render/background.rs +++ b/crates/rustmotion/src/engine/render/background.rs @@ -103,6 +103,10 @@ pub(super) fn draw_world_bg_with_parallax( } } +// The non-deprecated gradient API moves TileMode out of the signature; the +// transposition is tracked in #215, and doing it here without pixel tests +// would change rendering silently. +#[allow(deprecated)] fn draw_bg_gradient_shift( canvas: &Canvas, cfg: &GradientShiftConfig, From cf42208a5548fc4454f4f41537d4d16d1691a3be Mon Sep 17 00:00:00 2001 From: Baptiste Parmantier Date: Thu, 20 Aug 2026 11:29:46 +0200 Subject: [PATCH 2/2] refactor(skia): migrate off the deprecated gradient shader API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deprecated in skia-safe 0.93. Not a rename: Skia gained CSS Color 4 style interpolation, and the old `flags: u32` could only carry one boolean. The new model needs three orthogonal parameters — premultiplied or not, which colour space to interpolate in, and how to traverse hue — so the API was restructured around Gradient { Colors, Interpolation }. Rendering is preserved by construction rather than by hope: the old Flags::default() was empty(), the new Interpolation::default() is in_premul: No, and no site in this repository ever passed flags explicitly. Every call site uses Interpolation::default(), and no colour space is introduced anywhere it was not already present — shape.rs keeps the srgb space it declared before. TileMode did not disappear, it moved into Colors alongside the colours it applies to. One behaviour was implicit and had to be made explicit: the deprecated sweep() took an optional angle range, and None meant a full 0..360 turn. The replacement makes the tuple mandatory, so that range is now written at the call site. Closes #215 --- .../rustmotion-components/src/chart/line.rs | 17 ++-- .../src/gradient_text.rs | 19 ++-- crates/rustmotion-components/src/shape.rs | 41 ++++---- crates/rustmotion-components/src/skeleton.rs | 25 +++-- crates/rustmotion-components/src/sparkline.rs | 20 ++-- crates/rustmotion-components/src/stat.rs | 22 ++--- .../rustmotion-core/src/engine/paint_pass.rs | 96 +++++++------------ .../src/engine/render/background.rs | 30 +++--- 8 files changed, 108 insertions(+), 162 deletions(-) diff --git a/crates/rustmotion-components/src/chart/line.rs b/crates/rustmotion-components/src/chart/line.rs index b8b3ce4..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, PathBuilder, 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}; @@ -127,10 +128,6 @@ impl Chart { Ok(()) } - // The non-deprecated gradient API moves TileMode out of the signature; - // the transposition is tracked in #215, and doing it here without pixel - // tests would change rendering silently. - #[allow(deprecated)] pub(super) fn render_area(&self, canvas: &Canvas, w: f32, h: f32, progress: f32) -> Result<()> { let (mt, mr, mb, ml) = self.chart_margins(); let chart_w = w - ml - mr; @@ -231,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, ); diff --git a/crates/rustmotion-components/src/gradient_text.rs b/crates/rustmotion-components/src/gradient_text.rs index a3d210a..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, @@ -83,10 +84,6 @@ impl GradientText { } impl GradientText { - // The non-deprecated gradient API moves TileMode out of the signature; - // the transposition is tracked in #215, and doing it here without pixel - // tests would change rendering silently. - #[allow(deprecated)] fn paint( &self, canvas: &Canvas, @@ -219,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/shape.rs b/crates/rustmotion-components/src/shape.rs index c193e34..8844316 100644 --- a/crates/rustmotion-components/src/shape.rs +++ b/crates/rustmotion-components/src/shape.rs @@ -41,10 +41,6 @@ rustmotion_core::impl_traits!(Shape { }); impl Painter for Shape { - // The non-deprecated gradient API moves TileMode out of the signature; - // the transposition is tracked in #215, and doing it here without pixel - // tests would change rendering silently. - #[allow(deprecated)] fn paint_content( &self, canvas: &Canvas, @@ -87,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 0bbbfbf..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; @@ -85,10 +86,6 @@ rustmotion_core::impl_traits!(Skeleton { }); impl Skeleton { - // The non-deprecated gradient API moves TileMode out of the signature; - // the transposition is tracked in #215, and doing it here without pixel - // tests would change rendering silently. - #[allow(deprecated)] fn draw_shimmer_rect(&self, canvas: &Canvas, rect: Rect, radius: f32, time: f64) { let w = rect.width(); @@ -109,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 11794b0..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, PathBuilder, 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; @@ -102,10 +103,6 @@ impl Sparkline { 1.0 - (1.0 - p).powi(3) } - // The non-deprecated gradient API moves TileMode out of the signature; - // the transposition is tracked in #215, and doing it here without pixel - // tests would change rendering silently. - #[allow(deprecated)] fn paint(&self, canvas: &Canvas, layout_w: f32, layout_h: f32, time: f64) { let w = layout_w; let h = layout_h; @@ -156,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, ); diff --git a/crates/rustmotion-components/src/stat.rs b/crates/rustmotion-components/src/stat.rs index d8b7123..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, PathBuilder, 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; @@ -83,10 +86,6 @@ rustmotion_core::impl_traits!(Stat { }); impl Stat { - // The non-deprecated gradient API moves TileMode out of the signature; - // the transposition is tracked in #215, and doing it here without pixel - // tests would change rendering silently. - #[allow(deprecated)] fn paint(&self, canvas: &Canvas, layout_w: f32, layout_h: f32) { let w = layout_w; let h = layout_h; @@ -361,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 { diff --git a/crates/rustmotion-core/src/engine/paint_pass.rs b/crates/rustmotion-core/src/engine/paint_pass.rs index bbe0a3c..496cdea 100644 --- a/crates/rustmotion-core/src/engine/paint_pass.rs +++ b/crates/rustmotion-core/src/engine/paint_pass.rs @@ -17,6 +17,7 @@ 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, PathBuilder, Point, RRect, Rect, M44, V3, @@ -540,10 +541,6 @@ fn active_shimmer(css: &CssStyle, time: f64) -> Option<(&crate::schema::ShimmerC /// Stamp the sweeping band onto the layer built by steps 9-10, restricted to /// the pixels that layer actually painted. -// The non-deprecated gradient API moves TileMode out of the signature; the -// transposition is tracked in #215, and doing it here without pixel tests -// would change rendering silently. -#[allow(deprecated)] fn paint_shimmer_band( canvas: &Canvas, layout: &BoxLayout, @@ -576,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; }; @@ -1187,10 +1180,6 @@ fn paint_background( } } -// The non-deprecated gradient API moves TileMode out of the signature; the -// transposition is tracked in #215, and doing it here without pixel tests -// would change rendering silently. -#[allow(deprecated)] fn paint_bg_layer(canvas: &Canvas, rrect: &RRect, layer: &BackgroundLayer) { let mut paint = Paint::default(); paint.set_anti_alias(true); @@ -1203,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); } @@ -1223,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); } @@ -1244,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); } @@ -1263,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)); } @@ -1347,10 +1327,6 @@ fn paint_border( /// The gradient is linear along `gb.angle` with the **same angle convention as /// `background` linear gradients** (see [`gradient_endpoints`]) so the two /// stay visually consistent within one style block. Colors are evenly spaced. -// The non-deprecated gradient API moves TileMode out of the signature; the -// transposition is tracked in #215, and doing it here without pixel tests -// would change rendering silently. -#[allow(deprecated)] fn paint_gradient_border( canvas: &Canvas, layout: &BoxLayout, @@ -1385,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) @@ -1397,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; }; diff --git a/crates/rustmotion/src/engine/render/background.rs b/crates/rustmotion/src/engine/render/background.rs index cfc42fc..f7a27bb 100644 --- a/crates/rustmotion/src/engine/render/background.rs +++ b/crates/rustmotion/src/engine/render/background.rs @@ -103,10 +103,6 @@ pub(super) fn draw_world_bg_with_parallax( } } -// The non-deprecated gradient API moves TileMode out of the signature; the -// transposition is tracked in #215, and doing it here without pixel tests -// would change rendering silently. -#[allow(deprecated)] fn draw_bg_gradient_shift( canvas: &Canvas, cfg: &GradientShiftConfig, @@ -116,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; @@ -147,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) } };