Skip to content

Commit bd1e40e

Browse files
committed
chore: fixes & performance updates
1 parent 2b3f02e commit bd1e40e

164 files changed

Lines changed: 13800 additions & 7901 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ strip = true
2929
[workspace.dependencies.wgt]
3030
package = "wgpu-types"
3131
git = "https://github.com/triniwiz/wgpu"
32-
rev = "56a46b75f1b400446d0b1139b2435aa95a0d5ab5"
32+
rev = "9616414f16e5688192f6127f8104d8a07a3b1de6"
3333

3434

3535

@@ -45,9 +45,9 @@ canvas-webgl = { path = "./crates/canvas-webgl" }
4545
canvas-svg = { path = "./crates/canvas-svg" }
4646
gl-bindings = { path = "./crates/gl-bindings" }
4747
canvas-c = { path = "./crates/canvas-c" }
48-
skia-safe = { version = "0.87.0", features = ["textlayout"] }
48+
skia-safe = { version = "0.93.1", features = ["textlayout"] }
4949
itertools = "0.14.0"
50-
wgpu-core = { git = "https://github.com/triniwiz/wgpu", rev = "56a46b75f1b400446d0b1139b2435aa95a0d5ab5", features = ["wgsl", "vulkan", "metal", "raw-window-handle"] }
51-
wgpu-hal = { git = "https://github.com/triniwiz/wgpu", rev = "56a46b75f1b400446d0b1139b2435aa95a0d5ab5", features = ["metal", "vulkan"] }
50+
wgpu-core = { git = "https://github.com/triniwiz/wgpu", rev = "9616414f16e5688192f6127f8104d8a07a3b1de6", features = ["wgsl", "vulkan", "metal"] }
51+
wgpu-hal = { git = "https://github.com/triniwiz/wgpu", rev = "9616414f16e5688192f6127f8104d8a07a3b1de6", features = ["metal", "vulkan"] }
5252
ureq = "2.10.1"
5353
jni = "0.21.1"

Makefile

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ GENERATE_HEADERS:
2121
# --- iOS builds ---
2222
.PHONY: $(ARCHS_IOS)
2323
$(ARCHS_IOS): %:
24-
RUSTFLAGS="-Zlocation-detail=none -C panic=abort" \
24+
RUSTFLAGS="-Zlocation-detail=none -Zunstable-options -Cpanic=immediate-abort" \
2525
cargo +nightly build -Z build-std='std,panic_abort' \
26-
-Z build-std-features=panic_immediate_abort \
2726
--target $@ --release -p canvas-ios
2827

2928
$(XCFRAMEWORK): $(ARCHS_IOS)
@@ -39,9 +38,8 @@ GENERATE_ANDROID: $(ARCHS_ANDROID)
3938
# --- iOS SVG builds ---
4039
.PHONY: $(addsuffix _svg,$(ARCHS_IOS))
4140
$(addsuffix _svg,$(ARCHS_IOS)): %_svg:
42-
RUSTFLAGS="-Zlocation-detail=none -C panic=abort" \
41+
RUSTFLAGS="-Zlocation-detail=none -Zunstable-options -Cpanic=immediate-abort" \
4342
cargo +nightly build -Z build-std='std,panic_abort' \
44-
-Z build-std-features=panic_immediate_abort \
4543
--target $* --release -p canvas-svg-ios
4644

4745
.PHONY: GENERATE_IOS_SVG

apps/demo/src/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ require('@nativescript/canvas-polyfill');
44
*/
55
import '@nativescript/canvas-polyfill';
66
import { Canvas } from '@nativescript/canvas';
7+
78
/*
89
910

crates/canvas-2d/src/context/drawing_paths/fill_rule.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use skia_safe::path::FillType;
1+
use skia_safe::PathFillType;
22

33
#[derive(Copy, Clone, Debug)]
44
pub enum FillRule {
@@ -63,10 +63,10 @@ impl FillRule {
6363
_ => None,
6464
}
6565
}
66-
pub fn to_fill_type(&self) -> FillType {
66+
pub fn to_fill_type(&self) -> PathFillType {
6767
match self {
68-
FillRule::EvenOdd => FillType::EvenOdd,
69-
FillRule::NonZero => FillType::Winding,
68+
FillRule::EvenOdd => PathFillType::EvenOdd,
69+
FillRule::NonZero => PathFillType::Winding,
7070
}
7171
}
7272
}
Lines changed: 142 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
use std::borrow::BorrowMut;
2-
3-
use skia_safe::{ClipOp, Matrix, Point};
4-
51
use crate::context::drawing_paths::fill_rule::FillRule;
62
use crate::context::paths::path::Path;
73
use crate::context::Context;
4+
use skia_safe::{ClipOp, Color, Matrix, Point};
85

96
pub mod fill_rule;
107

@@ -15,55 +12,133 @@ impl Context {
1512
path: Option<&mut Path>,
1613
fill_rule: Option<FillRule>,
1714
) {
15+
#[cfg(any(target_os = "macos", target_os = "ios"))]
16+
let _ = unsafe { objc2_foundation::NSAutoreleasePool::new() };
17+
1818
#[cfg(feature = "gl")]
1919
{
2020
if let Some(ref context) = self.gl_context {
2121
context.make_current();
2222
}
2323
}
2424

25+
let current_rule = match path {
26+
None => self.path.fill_type(),
27+
Some(ref path) => path.fill_type(),
28+
};
29+
30+
let fill_rule = fill_rule.unwrap_or(FillRule::default());
31+
32+
let has_shadow = self.state.shadow_color != Color::TRANSPARENT
33+
&& self.state.shadow_blur > 0.0;
34+
35+
if has_shadow {
36+
// Slow path: need to save/modify/restore image filter + clone paint
37+
self.fill_or_stroke_with_shadow(is_fill, path, fill_rule, current_rule);
38+
} else {
39+
// Fast path: no shadow — skip all shadow overhead.
40+
let paint = if is_fill {
41+
self.state.paint.fill_paint.clone()
42+
} else {
43+
self.state.paint.stroke_paint.clone()
44+
};
45+
46+
let target_rule = fill_rule.to_fill_type();
47+
let needs_rule_change = current_rule != target_rule;
48+
49+
match path {
50+
Some(path) => {
51+
if needs_rule_change {
52+
path.set_fill_type_raw(target_rule);
53+
}
54+
self.render_to_canvas(&paint, |canvas, paint| {
55+
canvas.draw_path(path.path(), paint);
56+
});
57+
if needs_rule_change {
58+
path.set_fill_type_raw(current_rule);
59+
}
60+
}
61+
None => {
62+
if needs_rule_change {
63+
self.path.set_fill_type_raw(target_rule);
64+
}
65+
self.render_to_canvas_with_path(&paint, |canvas, paint, path| {
66+
canvas.draw_path(path.path(), paint);
67+
});
68+
if needs_rule_change {
69+
self.path.set_fill_type_raw(current_rule);
70+
}
71+
}
72+
};
73+
}
74+
}
75+
76+
#[cold]
77+
fn fill_or_stroke_with_shadow(
78+
&mut self,
79+
is_fill: bool,
80+
path: Option<&mut Path>,
81+
fill_rule: FillRule,
82+
current_rule: skia_safe::PathFillType,
83+
) {
84+
let saved_image_filter = if is_fill {
85+
self.state.paint.fill_paint.image_filter()
86+
} else {
87+
self.state.paint.stroke_paint.image_filter()
88+
};
89+
90+
let current_image_filter = if is_fill {
91+
self.state.paint.fill_paint.image_filter()
92+
} else {
93+
self.state.paint.stroke_paint.image_filter()
94+
};
95+
96+
let sigma = self.state.shadow_blur / 2.;
97+
98+
if let Some(shadow) = skia_safe::image_filters::drop_shadow_only(
99+
self.state.shadow_offset,
100+
(sigma, sigma),
101+
self.state.shadow_color,
102+
None,
103+
current_image_filter,
104+
None,
105+
) {
106+
if is_fill {
107+
self.state.paint.fill_paint.set_image_filter(shadow);
108+
} else {
109+
self.state.paint.stroke_paint.set_image_filter(shadow);
110+
};
111+
}
112+
25113
let paint = if is_fill {
26114
self.state.paint.fill_paint.clone()
27115
} else {
28116
self.state.paint.stroke_paint.clone()
29117
};
30118

31-
let fill_rule = fill_rule.unwrap_or(FillRule::default());
32-
33-
let path = match path {
119+
match path {
34120
Some(path) => {
35-
path.0.set_fill_type(fill_rule.to_fill_type());
36-
path.0.clone()
121+
path.set_fill_type_raw(fill_rule.to_fill_type());
122+
self.render_to_canvas(&paint, |canvas, paint| {
123+
canvas.draw_path(path.path(), paint);
124+
});
125+
path.set_fill_type_raw(current_rule);
37126
}
38127
None => {
39-
self.path.0.set_fill_type(fill_rule.to_fill_type());
40-
self.path.0.clone()
128+
self.path.set_fill_type_raw(fill_rule.to_fill_type());
129+
self.render_to_canvas_with_path(&paint, |canvas, paint, path| {
130+
canvas.draw_path(path.path(), paint);
131+
});
132+
self.path.set_fill_type_raw(current_rule);
41133
}
42134
};
43135

44-
let shadow_paint = if is_fill {
45-
self.state.paint.fill_shadow_paint(
46-
self.state.shadow_offset,
47-
self.state.shadow_color,
48-
self.state.shadow_blur,
49-
)
136+
// Restore the saved image filter
137+
if is_fill {
138+
self.state.paint.fill_paint.set_image_filter(saved_image_filter);
50139
} else {
51-
self.state.paint.stroke_shadow_paint(
52-
self.state.shadow_offset,
53-
self.state.shadow_color,
54-
self.state.shadow_blur,
55-
)
140+
self.state.paint.stroke_paint.set_image_filter(saved_image_filter);
56141
};
57-
58-
#[cfg(any(target_os = "macos", target_os = "ios"))]
59-
let pool = unsafe { objc2_foundation::NSAutoreleasePool::new() };
60-
61-
self.render_to_canvas(&paint, |canvas, paint| {
62-
if let Some(paint) = &shadow_paint {
63-
canvas.draw_path(&path, paint);
64-
}
65-
canvas.draw_path(&path, paint);
66-
});
67142
}
68143

69144
pub fn fill(&mut self, path: Option<&mut Path>) {
@@ -79,52 +154,62 @@ impl Context {
79154
}
80155

81156
pub fn clip(&mut self, path: Option<&mut Path>, fill_rule: Option<FillRule>) {
82-
let mut path = path
83-
.map(|path| path.clone())
84-
.unwrap_or_else(|| self.path.clone());
85-
86-
path.set_fill_type(fill_rule.unwrap_or(FillRule::NonZero));
87-
88-
self.surface
89-
.canvas()
90-
.clip_path(path.path(), Some(ClipOp::Intersect), Some(true));
157+
let fill_rule = fill_rule.unwrap_or(FillRule::NonZero);
158+
match path {
159+
Some(path) => {
160+
let current = path.fill_type();
161+
path.set_fill_type(fill_rule);
162+
self.surface
163+
.canvas()
164+
.clip_path(path.path(), Some(ClipOp::Intersect), Some(true));
165+
path.set_fill_type_raw(current);
166+
}
167+
None => {
168+
let current = self.path.fill_type();
169+
self.path.set_fill_type(fill_rule);
170+
self.surface
171+
.canvas()
172+
.clip_path(self.path.path(), Some(ClipOp::Intersect), Some(true));
173+
self.path.set_fill_type_raw(current);
174+
}
175+
}
91176
}
92177

93178
pub fn point_in_path(&self, path: Option<&Path>, x: f32, y: f32, rule: FillRule) -> bool {
94179
let path = path.unwrap_or(&self.path);
95180

96181
let total_matrix = self.state.matrix;
97-
let invertible = is_invertible(&total_matrix);
98-
if !invertible {
182+
if !is_invertible(&total_matrix) {
99183
return false;
100184
}
101185
if !x.is_finite() || !y.is_finite() {
102186
return false;
103187
}
104-
let matrix = total_matrix.clone();
105-
let inverse = matrix.invert().unwrap();
106-
let point: Point = (x, y).into();
107-
let transformed_point = inverse.map_point(point);
108-
let mut path_to_compare = path.path().clone();
109-
path_to_compare.set_fill_type(rule.to_fill_type());
110-
path_to_compare.contains(transformed_point)
188+
let inverse = total_matrix.invert().unwrap();
189+
let transformed_point = inverse.map_point(Point::new(x, y));
190+
let target_fill = rule.to_fill_type();
191+
let current_fill = path.path().fill_type();
192+
if current_fill == target_fill {
193+
path.path().contains(transformed_point)
194+
} else {
195+
let mut path_to_compare = path.path().clone();
196+
path_to_compare.set_fill_type(target_fill);
197+
path_to_compare.contains(transformed_point)
198+
}
111199
}
112200

113201
pub fn point_in_stroke(&self, path: Option<&Path>, x: f32, y: f32) -> bool {
114202
let path = path.unwrap_or(&self.path);
115203
let matrix = self.state.matrix;
116-
let invertible = is_invertible(&matrix);
117-
if !invertible {
204+
if !is_invertible(&matrix) {
118205
return false;
119206
}
120207
if !x.is_finite() || !y.is_finite() {
121208
return false;
122209
}
123210
let inverse = matrix.invert().unwrap();
124-
let point: Point = (x, y).into();
125-
let transformed_point = inverse.map_point(point);
126-
let path_to_compare = path.path();
127-
path_to_compare.contains(transformed_point)
211+
let transformed_point = inverse.map_point(Point::new(x, y));
212+
path.path().contains(transformed_point)
128213
}
129214
}
130215

@@ -134,5 +219,5 @@ fn det(matrix: &Matrix) -> f32 {
134219
}
135220

136221
fn is_invertible(matrix: &Matrix) -> bool {
137-
return det(matrix) != 0.0;
222+
det(matrix) != 0.0
138223
}

0 commit comments

Comments
 (0)