⬆️ Upgrade skia-safe to 0.153.3 with wasm SIMD (#12148)

Point SKIA_BINARIES_URL at the 0.153.3 simd prebuild and adapt
fonts, path stroke expansion, and gradients to the new API.
This commit is contained in:
Alejandro Alonso authored and GitHub committed 2026-10-07 12:08:33 +02:00
1 parent 9abcfebf60
commit cc5f2f57ad
14 files changed
+96 -63

No files matched your search

+42 -25
View File
@@ -23,6 +23,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
[[package]]
name = "bezier-rs"
version = "0.4.0"
@@ -49,7 +55,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
"syn",
"syn 2.0.106",
]
[[package]]
@@ -334,7 +340,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
"syn 2.0.106",
]
[[package]]
@@ -393,7 +399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "910d41a655dac3b764f1ade94821093d3610248694320cd072303a8eedcf221d"
dependencies = [
"proc-macro2",
"syn",
"syn 2.0.106",
]
[[package]]
@@ -425,9 +431,9 @@ dependencies = [
[[package]]
name = "regex"
version = "1.11.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
@@ -437,9 +443,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.8"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
@@ -448,15 +454,15 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.5"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "render"
version = "0.1.0"
dependencies = [
"base64",
"base64 0.22.1",
"bezier-rs",
"gl",
"glam",
@@ -525,7 +531,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.106",
]
[[package]]
@@ -563,9 +569,9 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]]
name = "skia-bindings"
version = "0.93.1"
version = "0.153.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2359f7e30c9da3f322f8ca3d4ec0abbc12a40035ce758309db0cdab07b5d4476"
checksum = "eb325787c91b3f6c34a7c6f2b8a2297b20292d733ca53a08c233e5028aec49af"
dependencies = [
"bindgen",
"cc",
@@ -580,11 +586,11 @@ dependencies = [
[[package]]
name = "skia-safe"
version = "0.93.1"
version = "0.153.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9e837ea9d531c9efee8f980bfcdb7226b21db0285b0c3171d8be745829f940"
checksum = "c61cb643dac43067f3eb76c866635a74377ac0abca0ff2590674e1e295514c3d"
dependencies = [
"base64",
"base64 0.23.1",
"bitflags",
"percent-encoding",
"skia-bindings",
@@ -593,13 +599,13 @@ dependencies = [
[[package]]
name = "skia-svg-macros"
version = "0.1.0"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "044dd2233c9717a74f75197f3e7f0a966db2127c0ffb5e05013b480a9b75b2c7"
checksum = "fec38aa7a389d249e1ce357b050ae5bdb302fd9943661fa650f39cf6ea51be97"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 3.0.6",
]
[[package]]
@@ -614,10 +620,21 @@ dependencies = [
]
[[package]]
name = "tar"
version = "0.4.42"
name = "syn"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ff6c40d3aedb5e06b57c6f669ad17ab063dd1e63d977c6a88e7f4dfa4f04020"
checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tar"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
dependencies = [
"filetime",
"libc",
@@ -654,7 +671,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.106",
]
[[package]]
@@ -740,7 +757,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn",
"syn 2.0.106",
"wasm-bindgen-shared",
]
@@ -762,7 +779,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.106",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
+1 -1
View File
@@ -26,7 +26,7 @@ gl = "0.14.0"
glam = "0.24.2"
indexmap = "2.7.1"
macros = { path = "macros" }
skia-safe = { version = "0.93.1", default-features = false, features = [
skia-safe = { version = "0.153.3", default-features = false, features = [
"gl",
"svg",
"textlayout",
+1 -1
View File
@@ -20,7 +20,7 @@ fi
export BUILD_NAME="${BUILD_NAME:-render-wasm}"
export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"};
export CARGO_TARGET_DIR=${CARGO_TARGET_DIR:-"target/$RENDER_TARGET"};
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.93.1/skia-binaries-319323662b1685a112f5-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp-pdf-simd.tar.gz"}
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.153.3/skia-binaries-b7f043e0b1e2a850e702-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp-pdf-simd.tar.gz"}
# 256 MB of initial heap to perform less
# initial calls to memory grow.
+1 -2
View File
@@ -10,8 +10,7 @@ fi
. ./_build_env frontend
export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"wasm32-unknown-emscripten"};
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.93.1/skia-binaries-319323662b1685a112f5-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp-pdf-simd.tar.gz"}
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.153.3/skia-binaries-b7f043e0b1e2a850e702-wasm32-unknown-emscripten-gl-svg-textlayout-binary-cache-webp-pdf-simd.tar.gz"}
ALLOWED_RULES="-D static_mut_refs"
+2 -1
View File
@@ -767,8 +767,9 @@ impl RenderState {
paint.set_style(skia::PaintStyle::Stroke);
paint.set_stroke_width(stroke_outset * 2.0);
let mut outline = skia::Path::default();
let mut outline = skia::PathBuilder::new();
if skia::path_utils::fill_path_with_paint(&base, &paint, &mut outline, None, None) {
let outline = outline.detach();
if let Some(united) = base.op(&outline, skia::PathOp::Union) {
return united;
}
+9 -4
View File
@@ -33,7 +33,12 @@ pub struct FontStore {
impl FontStore {
pub fn try_new() -> Result<Self> {
let font_mgr = FontMgr::new();
// custom_empty: FreeType-backed manager with no system scan. FontMgr::new()
// calls SkFontMgr_NewSystem, which on wasm tries to opendir font paths and
// aborts when Emscripten is built with -sFILESYSTEM=0 (exporter + browser).
let font_mgr = FontMgr::custom_empty().ok_or(Error::CriticalError(
"Failed to create empty FontMgr".to_string(),
))?;
let font_provider = load_default_provider(&font_mgr);
let mut font_collection = skia::textlayout::FontCollection::new();
font_collection.set_default_font_manager(FontMgr::from(font_provider.clone()), None);
@@ -47,7 +52,7 @@ impl FontStore {
let debug_font = skia::Font::new(debug_typeface, 12.0);
let ui_typeface = font_mgr
.new_from_data(UI_FONT_BYTES, None)
.new_from_data(skia::Data::new_copy(UI_FONT_BYTES), None)
.ok_or(Error::CriticalError("Failed to load UI font".to_string()))?;
let ui_font = skia::Font::new(ui_typeface, 12.0);
@@ -96,7 +101,7 @@ impl FontStore {
let typeface = self
.font_mgr
.new_from_data(font_data, None)
.new_from_data(skia::Data::new_copy(font_data), None)
.ok_or(Error::CriticalError(
"Failed to create typeface".to_string(),
))?;
@@ -265,7 +270,7 @@ fn load_default_provider(font_mgr: &FontMgr) -> skia::textlayout::TypefaceFontPr
let family = FontFamily::new(default_font_uuid(), 400, FontStyle::Normal);
let font = font_mgr
.new_from_data(DEFAULT_FONT_BYTES, None)
.new_from_data(skia::Data::new_copy(DEFAULT_FONT_BYTES), None)
.expect("Failed to load font");
font_provider.register_typeface(font, family.alias().as_str());
+3 -2
View File
@@ -186,8 +186,9 @@ const DEFAULT_SVG_SIZE: f32 = 512.0;
fn parse_svg(raw_data: &[u8]) -> Option<(skia::svg::Dom, Size)> {
// An empty font manager: <text> elements inside SVG image fills won't
// resolve typefaces. Wire the render state's font provider here if that
// ever becomes a need.
let font_mgr = skia::FontMgr::new();
// ever becomes a need. Must be custom_empty (not FontMgr::new): NewSystem
// scans font dirs and aborts under -sFILESYSTEM=0.
let font_mgr = skia::FontMgr::custom_empty()?;
let mut dom = skia::svg::Dom::from_bytes(raw_data, font_mgr).ok()?;
let mut size = dom.root().intrinsic_size();
+2 -2
View File
@@ -439,7 +439,7 @@ fn expand_side_patterns(
line_pb.detach()
};
let mut outline = skia::Path::default();
let mut outline = skia::PathBuilder::new();
if skia::path_utils::fill_path_with_paint(
&line_path,
&stamp_paint,
@@ -447,7 +447,7 @@ fn expand_side_patterns(
None,
None,
) {
pb.add_path(&outline);
pb.add_path(&outline.detach(), None);
expanded = true;
}
}
+6 -8
View File
@@ -364,14 +364,12 @@ fn draw_mask(ctx: &RenderCtx, rect: Rect, axis: MaskAxis, fade_to_end: bool) {
MaskAxis::Horizontal => (rect.right, rect.top),
MaskAxis::Vertical => (rect.left, rect.bottom),
};
let shader = skia::gradient_shader::linear(
((rect.left, rect.top), end),
&colors[..],
Some(&offsets[..]),
skia::TileMode::Clamp,
None,
None,
);
let colors4f: Vec<skia::Color4f> = colors.iter().map(|c| skia::Color4f::from(*c)).collect();
let gradient_colors =
skia::gradient::Colors::new(&colors4f, Some(&offsets[..]), skia::TileMode::Clamp, None);
let gradient =
skia::gradient::Gradient::new(gradient_colors, skia::gradient::Interpolation::default());
let shader = skia::shaders::linear_gradient(((rect.left, rect.top), end), &gradient, None);
let mut paint = Paint::default();
paint.set_anti_alias(false);
paint.set_style(PaintStyle::Fill);
+22 -11
View File
@@ -74,14 +74,20 @@ impl Gradient {
rect.left + self.end.0 * rect.width(),
rect.top + self.end.1 * rect.height(),
);
skia::gradient_shader::linear(
(start, end),
self.colors.as_slice(),
let colors4f: Vec<skia::Color4f> = self
.colors
.iter()
.map(|c| skia::Color4f::from(*c))
.collect();
let colors = skia::gradient::Colors::new(
&colors4f,
Some(self.offsets.as_slice()),
skia::TileMode::Clamp,
None,
None,
)
);
let gradient =
skia::gradient::Gradient::new(colors, skia::gradient::Interpolation::default());
skia::shaders::linear_gradient((start, end), &gradient, None)
}
pub fn to_radial_shader(&self, rect: &Rect) -> Option<skia::Shader> {
@@ -106,15 +112,20 @@ impl Gradient {
transform.pre_scale((self.width * rect.width() / rect.height(), 1.), None);
transform.pre_translate((-center.x, -center.y));
skia::gradient_shader::radial(
center,
distance,
self.colors.as_slice(),
let colors4f: Vec<skia::Color4f> = self
.colors
.iter()
.map(|c| skia::Color4f::from(*c))
.collect();
let colors = skia::gradient::Colors::new(
&colors4f,
Some(self.offsets.as_slice()),
skia::TileMode::Clamp,
None,
Some(&transform),
)
);
let gradient =
skia::gradient::Gradient::new(colors, skia::gradient::Interpolation::default());
skia::shaders::radial_gradient((center, distance), &gradient, Some(&transform))
}
}
+4 -3
View File
@@ -48,7 +48,7 @@ pub fn stroke_to_path(
paint.set_stroke_cap(cap);
}
let mut stroke_outline = skia::Path::default();
let mut stroke_outline = skia::PathBuilder::new();
let success = skia::path_utils::fill_path_with_paint(
&transformed_shape_path,
&paint,
@@ -60,6 +60,7 @@ pub fn stroke_to_path(
if !success {
return None;
}
let stroke_outline = stroke_outline.detach();
// For inner/outer strokes, use boolean ops to clip
// the 2×-width stroke outline to the correct region.
@@ -198,9 +199,9 @@ fn stroke_region(path: &skia::Path, width: f32) -> Option<skia::Path> {
paint.set_style(skia::PaintStyle::Stroke);
paint.set_stroke_width(width);
let mut outline = skia::Path::default();
let mut outline = skia::PathBuilder::new();
skia::path_utils::fill_path_with_paint(path, &paint, &mut outline, None, None)
.then_some(outline)
.then(|| outline.detach())
}
/// Filled geometry of a single stroke cap, matching what `handle_stroke_caps`
+1 -1
View File
@@ -68,7 +68,7 @@ impl TextPaths {
let Some(glyph_path) = font.get_path(*glyph) else {
continue;
};
builder.add_path(&glyph_path.with_offset(run_origin + *position));
builder.add_path(&glyph_path.with_offset(run_origin + *position), None);
has_glyphs = true;
}
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -x
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.93.1/skia-binaries-319323662b1685a112f5-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp.tar.gz"}
export SKIA_BINARIES_URL=${SKIA_BINARIES_URL:-"https://github.com/penpot/skia-binaries/releases/download/0.153.3/skia-binaries-b7f043e0b1e2a850e702-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp-pdf.tar.gz"}
export CARGO_BUILD_TARGET=${CARGO_BUILD_TARGET:-"x86_64-unknown-linux-gnu"};
_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
_SCRIPT_DIR=$(dirname $0);
export SKIA_BINARIES_URL="https://github.com/penpot/skia-binaries/releases/download/0.93.1/skia-binaries-319323662b1685a112f5-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp.tar.gz"
export SKIA_BINARIES_URL="https://github.com/penpot/skia-binaries/releases/download/0.153.3/skia-binaries-b7f043e0b1e2a850e702-x86_64-unknown-linux-gnu-gl-svg-textlayout-binary-cache-webp-pdf.tar.gz"
pushd $_SCRIPT_DIR;
cargo watch -x "test --bin render_wasm -- --show-output"