diff --git a/frontend/resources/wasm-playground/js/lib.js b/frontend/resources/wasm-playground/js/lib.js
index 5be649e7b..0f27cf83a 100644
--- a/frontend/resources/wasm-playground/js/lib.js
+++ b/frontend/resources/wasm-playground/js/lib.js
@@ -92,6 +92,74 @@ export function addShapeSolidFill(argb) {
Module._add_shape_fill();
}
+export function addShapeSolidStroleFill(argb) {
+ const ptr = allocBytes(160);
+ const heap = getHeapU32();
+ const dv = new DataView(heap.buffer);
+ dv.setUint8(ptr, 0x00, true);
+ dv.setUint32(ptr + 4, argb, true);
+ Module._add_shape_stroke_fill();
+}
+
+function serializePathAttrs(svgAttrs) {
+ return Object.entries(svgAttrs).reduce((acc, [key, value]) => {
+ return acc + key + '\0' + value + '\0';
+ }, '');
+}
+
+export function draw_star(x, y, width, height) {
+ const len = 11; // 1 MOVE + 9 LINE + 1 CLOSE
+ const ptr = allocBytes(len * 28);
+ const heap = getHeapU32();
+ const dv = new DataView(heap.buffer);
+
+ const cx = x + width / 2;
+ const cy = y + height / 2;
+ const outerRadius = Math.min(width, height) / 2;
+ const innerRadius = outerRadius * 0.4;
+
+ const star = [];
+ for (let i = 0; i < 10; i++) {
+ const angle = Math.PI / 5 * i - Math.PI / 2;
+ const r = i % 2 === 0 ? outerRadius : innerRadius;
+ const px = cx + r * Math.cos(angle);
+ const py = cy + r * Math.sin(angle);
+ star.push([px, py]);
+ }
+
+ let offset = 0;
+
+ // MOVE to first point
+ dv.setUint16(ptr + offset + 0, 1, true); // MOVE
+ dv.setFloat32(ptr + offset + 20, star[0][0], true);
+ dv.setFloat32(ptr + offset + 24, star[0][1], true);
+ offset += 28;
+
+ // LINE to remaining points
+ for (let i = 1; i < star.length; i++) {
+ dv.setUint16(ptr + offset + 0, 2, true); // LINE
+ dv.setFloat32(ptr + offset + 20, star[i][0], true);
+ dv.setFloat32(ptr + offset + 24, star[i][1], true);
+ offset += 28;
+ }
+
+ // CLOSE the path
+ dv.setUint16(ptr + offset + 0, 4, true); // CLOSE
+
+ Module._set_shape_path_content();
+
+ const str = serializePathAttrs({
+ "fill": "none",
+ "stroke-linecap": "round",
+ "stroke-linejoin": "round",
+ });
+ const size = str.length;
+ offset = allocBytes(size);
+ Module.stringToUTF8(str, offset, size);
+ Module._set_shape_path_attrs(3);
+}
+
+
export function setShapeChildren(shapeIds) {
const offset = allocBytes(shapeIds.length * 16);
const heap = getHeapU32();
diff --git a/frontend/resources/wasm-playground/paths.html b/frontend/resources/wasm-playground/paths.html
new file mode 100644
index 000000000..dd0cc13ca
--- /dev/null
+++ b/frontend/resources/wasm-playground/paths.html
@@ -0,0 +1,83 @@
+
+
+
+
+ WASM + WebGL2 Canvas
+
+
+
+
+
+
+
diff --git a/frontend/resources/wasm-playground/rects.html b/frontend/resources/wasm-playground/rects.html
index 322cadd64..92df83d54 100644
--- a/frontend/resources/wasm-playground/rects.html
+++ b/frontend/resources/wasm-playground/rects.html
@@ -26,13 +26,13 @@
import initWasmModule from '/js/render_wasm.js';
import {
init, addShapeSolidFill, assignCanvas, hexToU32ARGB, getRandomInt, getRandomColor,
- getRandomFloat, useShape, setShapeChildren, setupInteraction
+ getRandomFloat, useShape, setShapeChildren, setupInteraction, addShapeSolidStroleFill
} from './js/lib.js';
const canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
- const shapes = 100;
+ const shapes = 1000;
initWasmModule().then(Module => {
init(Module);
@@ -59,6 +59,10 @@
const color = getRandomColor();
const argb = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
addShapeSolidFill(argb)
+
+ Module._add_shape_center_stroke(10, 0, 0, 0);
+ const argb2 = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
+ addShapeSolidStroleFill(argb2);
}
useShape("00000000-0000-0000-0000-000000000000");
diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs
index 3c9d93383..3c59c04fd 100644
--- a/render-wasm/src/render.rs
+++ b/render-wasm/src/render.rs
@@ -1,6 +1,7 @@
use skia_safe::{self as skia, image, Matrix, RRect, Rect};
use crate::uuid::Uuid;
+use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
use crate::performance;
@@ -339,11 +340,11 @@ impl RenderState {
});
}
- // Clone so we don't change the value in the global state
- let mut shape = shape.clone();
+ // We don't want to change the value in the global state
+ let mut shape: Cow = Cow::Borrowed(shape);
if let Some(modifiers) = modifiers {
- shape.apply_transform(modifiers);
+ shape.to_mut().apply_transform(modifiers);
}
let center = shape.center();
@@ -365,7 +366,7 @@ impl RenderState {
match dom_result {
Ok(dom) => {
dom.render(self.surfaces.canvas(SurfaceId::Fills));
- shape.set_svg(dom);
+ shape.to_mut().set_svg(dom);
}
Err(e) => {
eprintln!("Error parsing SVG. Error: {}", e);
@@ -777,19 +778,24 @@ impl RenderState {
}
if !node_render_state.id.is_nil() {
- // If we didn't visited_children this shape, then we need to do
- let mut transformed_element = element.clone();
+ let mut transformed_element: Cow = Cow::Borrowed(element);
+
if let Some(modifier) = modifiers.get(&node_id) {
- transformed_element.apply_transform(modifier);
+ transformed_element.to_mut().apply_transform(modifier);
}
- if !transformed_element.extrect().intersects(self.render_area)
- || transformed_element.hidden()
- || transformed_element.visually_insignificant(self.get_scale())
- {
- debug::render_debug_shape(self, &transformed_element, false);
+
+ let is_visible = transformed_element
+ .extrect()
+ .intersects(self.render_area)
+ && !transformed_element.hidden
+ && !transformed_element.visually_insignificant(self.get_scale());
+
+ if self.options.is_debug_visible() {
+ debug::render_debug_shape(self, &transformed_element, is_visible);
+ }
+
+ if !is_visible {
continue;
- } else {
- debug::render_debug_shape(self, &transformed_element, true);
}
}
@@ -957,10 +963,10 @@ impl RenderState {
let mut nodes = vec![Uuid::nil()];
while let Some(shape_id) = nodes.pop() {
if let Some(shape) = tree.get_mut(&shape_id) {
- let mut shape = shape.clone();
+ let mut shape: Cow = Cow::Borrowed(shape);
if shape_id != Uuid::nil() {
if let Some(modifier) = modifiers.get(&shape_id) {
- shape.apply_transform(modifier);
+ shape.to_mut().apply_transform(modifier);
}
self.update_tile_for(&shape);
} else {
@@ -987,10 +993,10 @@ impl RenderState {
let mut nodes = vec![Uuid::nil()];
while let Some(shape_id) = nodes.pop() {
if let Some(shape) = tree.get_mut(&shape_id) {
- let mut shape = shape.clone();
+ let mut shape: Cow = Cow::Borrowed(shape);
if shape_id != Uuid::nil() {
if let Some(modifier) = modifiers.get(&shape_id) {
- shape.apply_transform(modifier);
+ shape.to_mut().apply_transform(modifier);
}
self.update_tile_for(&shape);
}
@@ -1011,8 +1017,8 @@ impl RenderState {
) {
for (uuid, matrix) in modifiers {
if let Some(shape) = tree.get_mut(uuid) {
- let mut shape: Shape = shape.clone();
- shape.apply_transform(matrix);
+ let mut shape: Cow = Cow::Borrowed(shape);
+ shape.to_mut().apply_transform(matrix);
self.update_tile_for(&shape);
}
}