mirror of https://github.com/penpot/penpot.git
✨ Improve setting svg attrs in wasm
This commit is contained in:
parent
dba718b850
commit
479ce99b32
File diff suppressed because it is too large
Load Diff
|
|
@ -211,3 +211,20 @@ test("Renders a file with a closed path shape with multiple segments using strok
|
||||||
|
|
||||||
await expect(workspace.canvas).toHaveScreenshot();
|
await expect(workspace.canvas).toHaveScreenshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test("Renders a file with paths and svg attrs", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const workspace = new WasmWorkspacePage(page);
|
||||||
|
await workspace.setupEmptyFile();
|
||||||
|
await workspace.mockGetFile("render-wasm/get-file-svg-attrs.json");
|
||||||
|
|
||||||
|
await workspace.goToWorkspace({
|
||||||
|
id: "4732f3e3-7a1a-807e-8006-ff76066e631d",
|
||||||
|
pageId: "4732f3e3-7a1a-807e-8006-ff76066e631e",
|
||||||
|
});
|
||||||
|
await workspace.waitForFirstRender();
|
||||||
|
|
||||||
|
await expect(workspace.canvas).toHaveScreenshot();
|
||||||
|
});
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
|
|
@ -370,12 +370,11 @@
|
||||||
(dissoc :style)
|
(dissoc :style)
|
||||||
(merge style)
|
(merge style)
|
||||||
(select-keys allowed-keys))
|
(select-keys allowed-keys))
|
||||||
str (sr/serialize-path-attrs attrs)
|
fill-rule (-> attrs :fillRule sr/translate-fill-rule)
|
||||||
size (count str)]
|
stroke-linecap (-> attrs :strokeLinecap sr/translate-stroke-linecap)
|
||||||
(when (pos? size)
|
stroke-linejoin (-> attrs :strokeLinejoin sr/translate-stroke-linejoin)
|
||||||
(let [offset (mem/alloc size)]
|
fill-none (= "none" (-> attrs :fill))]
|
||||||
(h/call wasm/internal-module "stringToUTF8" str offset size)
|
(h/call wasm/internal-module "_set_shape_svg_attrs" fill-rule stroke-linecap stroke-linejoin fill-none)))
|
||||||
(h/call wasm/internal-module "_set_shape_path_attrs" (count attrs))))))
|
|
||||||
|
|
||||||
(defn set-shape-path-content
|
(defn set-shape-path-content
|
||||||
"Upload path content in chunks to WASM."
|
"Upload path content in chunks to WASM."
|
||||||
|
|
@ -1160,7 +1159,10 @@
|
||||||
:text-direction (unchecked-get module "RawTextDirection")
|
:text-direction (unchecked-get module "RawTextDirection")
|
||||||
:text-decoration (unchecked-get module "RawTextDecoration")
|
:text-decoration (unchecked-get module "RawTextDecoration")
|
||||||
:text-transform (unchecked-get module "RawTextTransform")
|
:text-transform (unchecked-get module "RawTextTransform")
|
||||||
:segment-data (unchecked-get module "RawSegmentData")}]
|
:segment-data (unchecked-get module "RawSegmentData")
|
||||||
|
:stroke-linecap (unchecked-get module "RawStrokeLineCap")
|
||||||
|
:stroke-linejoin (unchecked-get module "RawStrokeLineJoin")
|
||||||
|
:fill-rule (unchecked-get module "RawFillRule")}]
|
||||||
(set! wasm/serializers serializers)
|
(set! wasm/serializers serializers)
|
||||||
(default))))
|
(default))))
|
||||||
(p/fmap (fn [default]
|
(p/fmap (fn [default]
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,24 @@
|
||||||
default (unchecked-get values "rect")]
|
default (unchecked-get values "rect")]
|
||||||
(d/nilv (unchecked-get values (d/name type)) default)))
|
(d/nilv (unchecked-get values (d/name type)) default)))
|
||||||
|
|
||||||
|
(defn translate-stroke-linecap
|
||||||
|
[stroke-linecap]
|
||||||
|
(let [values (unchecked-get wasm/serializers "stroke-linecap")
|
||||||
|
default (unchecked-get values "butt")]
|
||||||
|
(d/nilv (unchecked-get values (d/name stroke-linecap)) default)))
|
||||||
|
|
||||||
|
(defn translate-stroke-linejoin
|
||||||
|
[stroke-linejoin]
|
||||||
|
(let [values (unchecked-get wasm/serializers "stroke-linejoin")
|
||||||
|
default (unchecked-get values "miter")]
|
||||||
|
(d/nilv (unchecked-get values (d/name stroke-linejoin)) default)))
|
||||||
|
|
||||||
|
(defn translate-fill-rule
|
||||||
|
[fill-rule]
|
||||||
|
(let [values (unchecked-get wasm/serializers "fill-rule")
|
||||||
|
default (unchecked-get values "nonzero")]
|
||||||
|
(d/nilv (unchecked-get values (d/name fill-rule)) default)))
|
||||||
|
|
||||||
(defn translate-stroke-style
|
(defn translate-stroke-style
|
||||||
[stroke-style]
|
[stroke-style]
|
||||||
(let [values (unchecked-get wasm/serializers "stroke-style")
|
(let [values (unchecked-get wasm/serializers "stroke-style")
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,38 @@ Stroke styles are serialized as `u8`:
|
||||||
| 3 | Mixed |
|
| 3 | Mixed |
|
||||||
| \_ | Solid |
|
| \_ | Solid |
|
||||||
|
|
||||||
|
## Fill rules
|
||||||
|
|
||||||
|
Fill rules are serialized as `u8`
|
||||||
|
|
||||||
|
| Value | Field |
|
||||||
|
| ----- | ------ |
|
||||||
|
| 0 | Nonzero |
|
||||||
|
| 1 | Evenodd |
|
||||||
|
| \_ | Nonzero |
|
||||||
|
|
||||||
|
## Stroke linecaps
|
||||||
|
|
||||||
|
Stroke linecaps are serialized as `u8`
|
||||||
|
|
||||||
|
| Value | Field |
|
||||||
|
| ----- | ------ |
|
||||||
|
| 0 | Butt |
|
||||||
|
| 1 | Round |
|
||||||
|
| 2 | Square |
|
||||||
|
| \_ | Butt |
|
||||||
|
|
||||||
|
## Stroke linejoins
|
||||||
|
|
||||||
|
Stroke linejoins are serialized as `u8`
|
||||||
|
|
||||||
|
| Value | Field |
|
||||||
|
| ----- | ------ |
|
||||||
|
| 0 | Miter |
|
||||||
|
| 1 | Round |
|
||||||
|
| 2 | Bevel |
|
||||||
|
| \_ | Miter |
|
||||||
|
|
||||||
## Bool Operations
|
## Bool Operations
|
||||||
|
|
||||||
Bool operations (`bool-type`) are serialized as `u8`:
|
Bool operations (`bool-type`) are serialized as `u8`:
|
||||||
|
|
|
||||||
|
|
@ -764,14 +764,9 @@ impl RenderState {
|
||||||
&shape
|
&shape
|
||||||
};
|
};
|
||||||
|
|
||||||
let has_fill_none = matches!(
|
|
||||||
shape.svg_attrs.get("fill").map(String::as_str),
|
|
||||||
Some("none")
|
|
||||||
);
|
|
||||||
|
|
||||||
if shape.fills.is_empty()
|
if shape.fills.is_empty()
|
||||||
&& !matches!(shape.shape_type, Type::Group(_))
|
&& !matches!(shape.shape_type, Type::Group(_))
|
||||||
&& !has_fill_none
|
&& !shape.svg_attrs.fill_none
|
||||||
{
|
{
|
||||||
if let Some(fills_to_render) = self.nested_fills.last() {
|
if let Some(fills_to_render) = self.nested_fills.last() {
|
||||||
let fills_to_render = fills_to_render.clone();
|
let fills_to_render = fills_to_render.clone();
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::math::{Matrix, Point, Rect};
|
use crate::math::{Matrix, Point, Rect};
|
||||||
|
|
||||||
use crate::shapes::{Corners, Fill, ImageFill, Path, Shape, Stroke, StrokeCap, StrokeKind, Type};
|
use crate::shapes::{
|
||||||
|
Corners, Fill, ImageFill, Path, Shape, Stroke, StrokeCap, StrokeKind, SvgAttrs, Type,
|
||||||
|
};
|
||||||
use skia_safe::{self as skia, ImageFilter, RRect};
|
use skia_safe::{self as skia, ImageFilter, RRect};
|
||||||
|
|
||||||
use super::{RenderState, SurfaceId};
|
use super::{RenderState, SurfaceId};
|
||||||
|
|
@ -17,7 +17,7 @@ fn draw_stroke_on_rect(
|
||||||
rect: &Rect,
|
rect: &Rect,
|
||||||
selrect: &Rect,
|
selrect: &Rect,
|
||||||
corners: &Option<Corners>,
|
corners: &Option<Corners>,
|
||||||
svg_attrs: &HashMap<String, String>,
|
svg_attrs: &SvgAttrs,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
shadow: Option<&ImageFilter>,
|
shadow: Option<&ImageFilter>,
|
||||||
blur: Option<&ImageFilter>,
|
blur: Option<&ImageFilter>,
|
||||||
|
|
@ -53,7 +53,7 @@ fn draw_stroke_on_circle(
|
||||||
stroke: &Stroke,
|
stroke: &Stroke,
|
||||||
rect: &Rect,
|
rect: &Rect,
|
||||||
selrect: &Rect,
|
selrect: &Rect,
|
||||||
svg_attrs: &HashMap<String, String>,
|
svg_attrs: &SvgAttrs,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
shadow: Option<&ImageFilter>,
|
shadow: Option<&ImageFilter>,
|
||||||
blur: Option<&ImageFilter>,
|
blur: Option<&ImageFilter>,
|
||||||
|
|
@ -130,7 +130,7 @@ pub fn draw_stroke_on_path(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
selrect: &Rect,
|
selrect: &Rect,
|
||||||
path_transform: Option<&Matrix>,
|
path_transform: Option<&Matrix>,
|
||||||
svg_attrs: &HashMap<String, String>,
|
svg_attrs: &SvgAttrs,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
shadow: Option<&ImageFilter>,
|
shadow: Option<&ImageFilter>,
|
||||||
blur: Option<&ImageFilter>,
|
blur: Option<&ImageFilter>,
|
||||||
|
|
@ -217,7 +217,7 @@ fn handle_stroke_caps(
|
||||||
selrect: &Rect,
|
selrect: &Rect,
|
||||||
canvas: &skia::Canvas,
|
canvas: &skia::Canvas,
|
||||||
is_open: bool,
|
is_open: bool,
|
||||||
svg_attrs: &HashMap<String, String>,
|
svg_attrs: &SvgAttrs,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
blur: Option<&ImageFilter>,
|
blur: Option<&ImageFilter>,
|
||||||
antialias: bool,
|
antialias: bool,
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ mod rects;
|
||||||
mod shadows;
|
mod shadows;
|
||||||
mod shape_to_path;
|
mod shape_to_path;
|
||||||
mod strokes;
|
mod strokes;
|
||||||
|
mod svg_attrs;
|
||||||
mod svgraw;
|
mod svgraw;
|
||||||
mod text;
|
mod text;
|
||||||
pub mod text_paths;
|
pub mod text_paths;
|
||||||
|
|
@ -41,6 +42,7 @@ pub use rects::*;
|
||||||
pub use shadows::*;
|
pub use shadows::*;
|
||||||
pub use shape_to_path::*;
|
pub use shape_to_path::*;
|
||||||
pub use strokes::*;
|
pub use strokes::*;
|
||||||
|
pub use svg_attrs::*;
|
||||||
pub use svgraw::*;
|
pub use svgraw::*;
|
||||||
pub use text::*;
|
pub use text::*;
|
||||||
pub use transform::*;
|
pub use transform::*;
|
||||||
|
|
@ -174,7 +176,7 @@ pub struct Shape {
|
||||||
pub opacity: f32,
|
pub opacity: f32,
|
||||||
pub hidden: bool,
|
pub hidden: bool,
|
||||||
pub svg: Option<skia::svg::Dom>,
|
pub svg: Option<skia::svg::Dom>,
|
||||||
pub svg_attrs: HashMap<String, String>,
|
pub svg_attrs: SvgAttrs,
|
||||||
pub shadows: Vec<Shadow>,
|
pub shadows: Vec<Shadow>,
|
||||||
pub layout_item: Option<LayoutItem>,
|
pub layout_item: Option<LayoutItem>,
|
||||||
pub extrect: OnceCell<math::Rect>,
|
pub extrect: OnceCell<math::Rect>,
|
||||||
|
|
@ -201,7 +203,7 @@ impl Shape {
|
||||||
hidden: false,
|
hidden: false,
|
||||||
blur: None,
|
blur: None,
|
||||||
svg: None,
|
svg: None,
|
||||||
svg_attrs: HashMap::new(),
|
svg_attrs: SvgAttrs::default(),
|
||||||
shadows: Vec::with_capacity(1),
|
shadows: Vec::with_capacity(1),
|
||||||
layout_item: None,
|
layout_item: None,
|
||||||
extrect: OnceCell::new(),
|
extrect: OnceCell::new(),
|
||||||
|
|
@ -566,15 +568,6 @@ impl Shape {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_path_attr(&mut self, name: String, value: String) {
|
|
||||||
match self.shape_type {
|
|
||||||
Type::Path(_) | Type::Bool(_) => {
|
|
||||||
self.set_svg_attr(name, value);
|
|
||||||
}
|
|
||||||
_ => unreachable!("This shape should have path attrs"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_svg_raw_content(&mut self, content: String) -> Result<(), String> {
|
pub fn set_svg_raw_content(&mut self, content: String) -> Result<(), String> {
|
||||||
self.shape_type = Type::SVGRaw(SVGRaw::from_content(content));
|
self.shape_type = Type::SVGRaw(SVGRaw::from_content(content));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -607,10 +600,6 @@ impl Shape {
|
||||||
self.svg = Some(svg);
|
self.svg = Some(svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_svg_attr(&mut self, name: String, value: String) {
|
|
||||||
self.svg_attrs.insert(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn blend_mode(&self) -> BlendMode {
|
pub fn blend_mode(&self) -> BlendMode {
|
||||||
self.blend_mode
|
self.blend_mode
|
||||||
}
|
}
|
||||||
|
|
@ -1104,7 +1093,7 @@ impl Shape {
|
||||||
if let Some(path_transform) = self.to_path_transform() {
|
if let Some(path_transform) = self.to_path_transform() {
|
||||||
skia_path.transform(&path_transform);
|
skia_path.transform(&path_transform);
|
||||||
}
|
}
|
||||||
if let Some("evenodd") = self.svg_attrs.get("fill-rule").map(String::as_str) {
|
if self.svg_attrs.fill_rule == FillRule::Evenodd {
|
||||||
skia_path.set_fill_type(skia::PathFillType::EvenOdd);
|
skia_path.set_fill_type(skia::PathFillType::EvenOdd);
|
||||||
}
|
}
|
||||||
Some(skia_path)
|
Some(skia_path)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::shapes::fills::{Fill, SolidColor};
|
use crate::shapes::fills::{Fill, SolidColor};
|
||||||
use skia_safe::{self as skia, Rect};
|
use skia_safe::{self as skia, Rect};
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use super::Corners;
|
use super::Corners;
|
||||||
|
use super::StrokeLineCap;
|
||||||
|
use super::StrokeLineJoin;
|
||||||
|
use super::SvgAttrs;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||||
pub enum StrokeStyle {
|
pub enum StrokeStyle {
|
||||||
|
|
@ -159,7 +161,7 @@ impl Stroke {
|
||||||
pub fn to_paint(
|
pub fn to_paint(
|
||||||
&self,
|
&self,
|
||||||
rect: &Rect,
|
rect: &Rect,
|
||||||
svg_attrs: &HashMap<String, String>,
|
svg_attrs: &SvgAttrs,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
antialias: bool,
|
antialias: bool,
|
||||||
) -> skia::Paint {
|
) -> skia::Paint {
|
||||||
|
|
@ -175,11 +177,11 @@ impl Stroke {
|
||||||
paint.set_stroke_width(width);
|
paint.set_stroke_width(width);
|
||||||
paint.set_anti_alias(antialias);
|
paint.set_anti_alias(antialias);
|
||||||
|
|
||||||
if let Some("round") = svg_attrs.get("stroke-linecap").map(String::as_str) {
|
if svg_attrs.stroke_linecap == StrokeLineCap::Round {
|
||||||
paint.set_stroke_cap(skia::paint::Cap::Round);
|
paint.set_stroke_cap(skia::paint::Cap::Round);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some("round") = svg_attrs.get("stroke-linejoin").map(String::as_str) {
|
if svg_attrs.stroke_linejoin == StrokeLineJoin::Round {
|
||||||
paint.set_stroke_join(skia::paint::Join::Round);
|
paint.set_stroke_join(skia::paint::Join::Round);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,7 +227,7 @@ impl Stroke {
|
||||||
&self,
|
&self,
|
||||||
is_open: bool,
|
is_open: bool,
|
||||||
rect: &Rect,
|
rect: &Rect,
|
||||||
svg_attrs: &HashMap<String, String>,
|
svg_attrs: &SvgAttrs,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
antialias: bool,
|
antialias: bool,
|
||||||
) -> skia::Paint {
|
) -> skia::Paint {
|
||||||
|
|
@ -249,7 +251,7 @@ impl Stroke {
|
||||||
&self,
|
&self,
|
||||||
is_open: bool,
|
is_open: bool,
|
||||||
rect: &Rect,
|
rect: &Rect,
|
||||||
svg_attrs: &HashMap<String, String>,
|
svg_attrs: &SvgAttrs,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
antialias: bool,
|
antialias: bool,
|
||||||
) -> skia::Paint {
|
) -> skia::Paint {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
#[derive(Debug, Clone, PartialEq, Copy, Default)]
|
||||||
|
pub enum FillRule {
|
||||||
|
#[default]
|
||||||
|
Nonzero,
|
||||||
|
Evenodd,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Copy, Default)]
|
||||||
|
pub enum StrokeLineCap {
|
||||||
|
#[default]
|
||||||
|
Butt,
|
||||||
|
Round,
|
||||||
|
Square,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Copy, Default)]
|
||||||
|
pub enum StrokeLineJoin {
|
||||||
|
#[default]
|
||||||
|
Miter,
|
||||||
|
Round,
|
||||||
|
Bevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||||
|
pub struct SvgAttrs {
|
||||||
|
pub fill_rule: FillRule,
|
||||||
|
pub stroke_linecap: StrokeLineCap,
|
||||||
|
pub stroke_linejoin: StrokeLineJoin,
|
||||||
|
/// Indicates that this shape has an explicit `fill="none"` attribute.
|
||||||
|
///
|
||||||
|
/// In SVG, the `fill` attribute is inheritable from container elements like `<g>`.
|
||||||
|
/// However, when a shape explicitly sets `fill="none"`, it breaks the color
|
||||||
|
/// inheritance chain - the shape will not inherit fill colors from parent containers.
|
||||||
|
///
|
||||||
|
/// This is different from having an empty fills array, as it explicitly signals
|
||||||
|
/// the intention to have no fill, preventing inheritance.
|
||||||
|
pub fill_none: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SvgAttrs {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
fill_rule: FillRule::Nonzero,
|
||||||
|
stroke_linecap: StrokeLineCap::Butt,
|
||||||
|
stroke_linejoin: StrokeLineJoin::Miter,
|
||||||
|
fill_none: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,4 +7,5 @@ pub mod paths;
|
||||||
pub mod shadows;
|
pub mod shadows;
|
||||||
pub mod shapes;
|
pub mod shapes;
|
||||||
pub mod strokes;
|
pub mod strokes;
|
||||||
|
pub mod svg_attrs;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
|
||||||
|
|
@ -225,37 +225,6 @@ pub extern "C" fn current_to_path() -> *mut u8 {
|
||||||
mem::write_vec(result)
|
mem::write_vec(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extracts a string from the bytes slice until the next null byte (0) and returns the result as a `String`.
|
|
||||||
// Updates the `start` index to the end of the extracted string.
|
|
||||||
fn extract_string(start: &mut usize, bytes: &[u8]) -> String {
|
|
||||||
match bytes[*start..].iter().position(|&b| b == 0) {
|
|
||||||
Some(pos) => {
|
|
||||||
let end = *start + pos;
|
|
||||||
let slice = &bytes[*start..end];
|
|
||||||
*start = end + 1; // Move the `start` pointer past the null byte
|
|
||||||
// Call to unsafe function within an unsafe block
|
|
||||||
unsafe { String::from_utf8_unchecked(slice.to_vec()) }
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
*start = bytes.len(); // Move `start` to the end if no null byte is found
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn set_shape_path_attrs(num_attrs: u32) {
|
|
||||||
with_current_shape_mut!(state, |shape: &mut Shape| {
|
|
||||||
let bytes = mem::bytes();
|
|
||||||
let mut start = 0;
|
|
||||||
for _ in 0..num_attrs {
|
|
||||||
let name = extract_string(&mut start, &bytes);
|
|
||||||
let value = extract_string(&mut start, &bytes);
|
|
||||||
shape.set_path_attr(name, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
use macros::ToJs;
|
||||||
|
|
||||||
|
use crate::shapes::{FillRule, StrokeLineCap, StrokeLineJoin};
|
||||||
|
use crate::{with_current_shape_mut, STATE};
|
||||||
|
|
||||||
|
#[derive(PartialEq, ToJs)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum RawFillRule {
|
||||||
|
Nonzero = 0,
|
||||||
|
Evenodd = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for RawFillRule {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
unsafe { std::mem::transmute(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RawFillRule> for FillRule {
|
||||||
|
fn from(value: RawFillRule) -> Self {
|
||||||
|
match value {
|
||||||
|
RawFillRule::Nonzero => FillRule::Nonzero,
|
||||||
|
RawFillRule::Evenodd => FillRule::Evenodd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, ToJs)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum RawStrokeLineCap {
|
||||||
|
Butt = 0,
|
||||||
|
Round = 1,
|
||||||
|
Square = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for RawStrokeLineCap {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
unsafe { std::mem::transmute(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RawStrokeLineCap> for StrokeLineCap {
|
||||||
|
fn from(value: RawStrokeLineCap) -> Self {
|
||||||
|
match value {
|
||||||
|
RawStrokeLineCap::Butt => StrokeLineCap::Butt,
|
||||||
|
RawStrokeLineCap::Round => StrokeLineCap::Round,
|
||||||
|
RawStrokeLineCap::Square => StrokeLineCap::Square,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, ToJs)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum RawStrokeLineJoin {
|
||||||
|
Miter = 0,
|
||||||
|
Round = 1,
|
||||||
|
Bevel = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for RawStrokeLineJoin {
|
||||||
|
fn from(value: u8) -> Self {
|
||||||
|
unsafe { std::mem::transmute(value) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RawStrokeLineJoin> for StrokeLineJoin {
|
||||||
|
fn from(value: RawStrokeLineJoin) -> Self {
|
||||||
|
match value {
|
||||||
|
RawStrokeLineJoin::Miter => StrokeLineJoin::Miter,
|
||||||
|
RawStrokeLineJoin::Round => StrokeLineJoin::Round,
|
||||||
|
RawStrokeLineJoin::Bevel => StrokeLineJoin::Bevel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn set_shape_svg_attrs(
|
||||||
|
fill_rule: u8,
|
||||||
|
stroke_linecap: u8,
|
||||||
|
stroke_linejoin: u8,
|
||||||
|
fill_none: bool,
|
||||||
|
) {
|
||||||
|
with_current_shape_mut!(state, |shape: &mut Shape| {
|
||||||
|
let fill_rule = RawFillRule::from(fill_rule);
|
||||||
|
shape.svg_attrs.fill_rule = fill_rule.into();
|
||||||
|
let stroke_linecap = RawStrokeLineCap::from(stroke_linecap);
|
||||||
|
shape.svg_attrs.stroke_linecap = stroke_linecap.into();
|
||||||
|
let stroke_linejoin = RawStrokeLineJoin::from(stroke_linejoin);
|
||||||
|
shape.svg_attrs.stroke_linejoin = stroke_linejoin.into();
|
||||||
|
shape.svg_attrs.fill_none = fill_none;
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue