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 061d4b02..b19c078d 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 7ed52bdd..381f5f72 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 f014a120..e585886e 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 74cefae2..22e8bf97 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 60e9122b..edc0fea5 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 16951d68..b8b3ce4f 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 54065fbc..b055ea8f 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 de05e74d..954ab5cf 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 1c0710f6..68d4c580 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 0350cb70..22f195d7 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 ac11cc5c..a3d210ae 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 ad291898..8520a92f 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 c8458465..535e7a95 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 9d0b9132..1b1fb199 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 85af5c74..98efd5a4 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 df6ffc15..c193e342 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 b9bbe27f..0bbbfbff 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 e5fde0fd..11794b06 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 740ed5d0..d8b7123b 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 255792eb..239118a4 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 824e6e65..ccf9e242 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 61299b53..44cd1a54 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 8f7e7df8..07ff6bdb 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 34e984d1..4999d582 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 89e9331f..edcaedf0 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 0255ce40..bbe0a3c7 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 895f9845..48156aa2 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 da1859a1..b256a080 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 fad36df4..00ff3128 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 a50db3ff..cfc42fc5 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 b8b3ce4f..3230efb2 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 a3d210ae..13114787 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 c193e342..88443163 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 0bbbfbff..0106344f 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 11794b06..da125f3f 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 d8b7123b..fe181b1f 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 bbe0a3c7..496cdea0 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 cfc42fc5..f7a27bb3 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) } };