🐛 Set evenodd when needed on stroke to path (#10446)

This commit is contained in:
Elena Torró 2026-06-26 15:39:46 +02:00 committed by GitHub
parent 86a8dcec2c
commit e03a852fb2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 46 additions and 18 deletions

View File

@ -104,17 +104,19 @@
(into []
(keep-indexed
(fn [idx stroke]
(let [content (wasm.api/stroke-to-path (:id shape) idx)]
(when (some? content)
(let [result (wasm.api/stroke-to-path (:id shape) idx)]
(when (some? result)
(cts/setup-shape
{:type :path
:id (uuid/next)
:name (str (:name shape) " (stroke)")
:parent-id parent-id
:frame-id frame-id
:content content
:fills [(stroke->fill stroke)]
:strokes []})))))
(cond-> {:type :path
:id (uuid/next)
:name (str (:name shape) " (stroke)")
:parent-id parent-id
:frame-id frame-id
:content (:content result)
:fills [(stroke->fill stroke)]
:strokes []}
(:even-odd? result)
(assoc :svg-attrs {:fillRule "evenodd"})))))))
(:strokes shape)))
(defn convert-selected-strokes-to-path

View File

@ -2272,21 +2272,25 @@
(defn stroke-to-path
"Converts a shape's stroke at the given index into a filled path.
Returns the stroke outline as PathData content."
Returns a map {:content <PathData> :even-odd? <boolean>}, or nil when the
stroke produces no geometry. The buffer carries two header words ahead of
the segments: [even-odd flag][length] (the flat segment list can't encode
the fill rule itself)."
[id stroke-index]
(use-shape id)
(try
(let [offset (-> (h/call wasm/internal-module "_convert_stroke_to_path" stroke-index)
(mem/->offset-32))
heap (mem/get-heap-u32)
length (aget heap offset)]
(let [offset (-> (h/call wasm/internal-module "_convert_stroke_to_path" stroke-index)
(mem/->offset-32))
heap (mem/get-heap-u32)
even-odd? (not (zero? (aget heap offset)))
length (aget heap (inc offset))]
(if (pos? length)
(let [data (mem/slice heap
(+ offset 1)
(+ offset 2)
(* length path.impl/SEGMENT-U32-SIZE))
content (path/from-bytes data)]
(mem/free)
content)
{:content content :even-odd? even-odd?})
(do (mem/free)
nil)))
(catch :default cause

View File

@ -95,3 +95,23 @@ pub fn write_vec<T: SerializableResult>(result: Vec<T>) -> *mut u8 {
write_bytes(result_bytes)
}
/*
Like `write_vec`, but prepends an extra u32 header word before the
length. Layout: [header u32][length u32][items...]
*/
pub fn write_vec_with_header<T: SerializableResult>(header: u32, result: Vec<T>) -> *mut u8 {
let elem_size = size_of::<T::BytesType>();
let bytes_len = 8 + result.len() * elem_size;
let mut result_bytes = vec![0; bytes_len];
result_bytes[0..4].clone_from_slice(&header.to_le_bytes());
result_bytes[4..8].clone_from_slice(&result.len().to_le_bytes());
for (i, item) in result.iter().enumerate() {
let base = 8 + i * elem_size;
item.clone_to_slice(&mut result_bytes[base..base + elem_size]);
}
write_bytes(result_bytes)
}

View File

@ -243,6 +243,7 @@ pub extern "C" fn current_to_path() -> *mut u8 {
#[no_mangle]
pub extern "C" fn convert_stroke_to_path(stroke_index: i32) -> *mut u8 {
let mut result = Vec::<RawSegmentData>::default();
let mut even_odd = false;
with_current_shape!(state, |shape: &Shape| {
let idx = stroke_index as usize;
if let Some(stroke) = shape.strokes.get(idx) {
@ -257,6 +258,7 @@ pub extern "C" fn convert_stroke_to_path(stroke_index: i32) -> *mut u8 {
shape.svg_attrs.as_ref(),
false,
) {
even_odd = path.is_even_odd();
result = path
.segments()
.iter()
@ -267,7 +269,7 @@ pub extern "C" fn convert_stroke_to_path(stroke_index: i32) -> *mut u8 {
}
});
mem::write_vec(result)
mem::write_vec_with_header(even_odd as u32, result)
}
#[cfg(test)]