diff --git a/soft-skia-wasm/src/lib.rs b/soft-skia-wasm/src/lib.rs
index e4df79e..3be1240 100644
--- a/soft-skia-wasm/src/lib.rs
+++ b/soft-skia-wasm/src/lib.rs
@@ -1,10 +1,13 @@
extern crate soft_skia;
mod utils;
+use std::collections::HashMap;
+
use base64;
+use soft_skia::provider::{Providers, Group, Provider, GroupClip};
use wasm_bindgen::prelude::*;
use soft_skia::instance::Instance;
-use soft_skia::shape::{Circle, Line, Points, RoundRect, Shapes, PaintStyle};
+use soft_skia::shape::{Circle, Line, Points, RoundRect, Shapes, PaintStyle, DrawContext};
use soft_skia::shape::Rect;
use soft_skia::shape::ColorU8;
use soft_skia::tree::Node;
@@ -29,8 +32,8 @@ pub struct WASMRectAttr {
height: u32,
x: u32,
y: u32,
- color: String,
- style: String,
+ color: Option,
+ style: Option,
}
#[derive(Serialize, Deserialize, Debug)]
@@ -38,8 +41,8 @@ pub struct WASMCircleAttr {
cx: u32,
cy: u32,
r: u32,
- color: String,
- style: String,
+ color: Option,
+ style: Option,
}
#[derive(Serialize, Deserialize, Debug)]
@@ -49,24 +52,39 @@ pub struct WASMRoundRectAttr {
r: u32,
x: u32,
y: u32,
- color: String,
- style: String,
+ color: Option,
+ style: Option,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct WASMLineAttr {
p1: [u32; 2],
p2: [u32; 2],
- color: String,
- stroke_width: u32
+ color: Option,
+ stroke_width: Option
}
#[derive(Serialize, Deserialize, Debug)]
pub struct WASMPointsAttr {
points: Vec<[u32; 2]>,
- color: String,
- stroke_width: u32,
- style: String,
+ color: Option,
+ stroke_width: Option,
+ style: Option,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct WASMGroupAttr {
+ x: Option,
+ y: Option,
+ color: Option,
+ style: Option,
+ stroke_width: Option,
+ invert_clip: Option,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct WASMGroupClipAttr {
+ clip: usize,
}
#[derive(Serialize, Deserialize, Debug)]
@@ -76,6 +94,8 @@ pub enum WASMShapesAttr {
RR(WASMRoundRectAttr),
L(WASMLineAttr),
P(WASMPointsAttr),
+ G(WASMGroupAttr),
+ GC(WASMGroupClipAttr),
}
#[derive(Serialize, Deserialize, Debug)]
@@ -132,146 +152,139 @@ impl SoftSkiaWASM {
}
fn recursive_rasterization_node_to_pixmap(node: &mut Node, pixmap: &mut Pixmap) -> () {
- for item in node.children_iter_mut() {
- item.shape.draw(pixmap);
- Self::recursive_rasterization_node_to_pixmap(&mut (*item), pixmap);
- }
- }
-
+ let context = node.provider.as_ref().and_then(|p| p.get_context());
- #[wasm_bindgen(js_name = setShapeBySerde)]
- pub fn set_shape_by_serde(&mut self, id: usize, value: JsValue) {
- let message: WASMShape = serde_wasm_bindgen::from_value(value).unwrap();
+ for item in node.children.iter_mut() {
+ let inactive = *context
+ .and_then(|c| c.inactive_nodes_map.as_ref().and_then(|m| m.get(&item.id)))
+ .unwrap_or(&false);
- match message.attr {
- WASMShapesAttr::R(WASMRectAttr{ width, height, x, y , color, style}) => {
+ if inactive {
+ continue;
+ }
- let mut parser_input = ParserInput::new(&color);
- let mut parser = Parser::new(&mut parser_input);
- let color = CSSColor::parse(&mut parser);
-
- match color {
- Ok(CSSColor::RGBA(rgba)) => {
- drop(parser_input);
- let style = match style.as_str() {
- "stroke" => {
- PaintStyle::Stroke
- },
- "fill" => {
- PaintStyle::Fill
- },
- _ => {
- PaintStyle::Stroke
+ item.draw(pixmap, context);
+
+ if let Some(provider) = item.provider.as_mut() {
+ provider.set_context(pixmap, node.provider.as_ref());
+
+ // 这段感觉放这有点重,想搬走
+ match provider {
+ Providers::G(group) => {
+ if let Some(clip) = &group.clip {
+ if let Some(clip_id) = clip.id {
+ if let Some(clip_path) = item
+ .children
+ .iter_mut()
+ .find(|n| n.id == clip_id)
+ .and_then(|n| Some(n.shape.get_path(group.context.as_ref().unwrap())))
+ {
+ group.set_context_mask(pixmap, &clip_path);
+ }
}
- };
- self.0.set_shape_to_child(id, Shapes::R(Rect { x, y, width, height, color: ColorU8::from_rgba(rgba.red, rgba.green, rgba.blue, rgba.alpha), style }))
- }
- _ => {
- //
+ }
}
}
- },
- WASMShapesAttr::C(WASMCircleAttr{ cx, cy, r, color, style }) => {
+ }
- let mut parser_input = ParserInput::new(&color);
- let mut parser = Parser::new(&mut parser_input);
- let color = CSSColor::parse(&mut parser);
-
- match color {
- Ok(CSSColor::RGBA(rgba)) => {
- drop(parser_input);
- let style = match style.as_str() {
- "stroke" => {
- PaintStyle::Stroke
- },
- "fill" => {
- PaintStyle::Fill
- },
- _ => {
- PaintStyle::Stroke
- }
- };
- self.0.set_shape_to_child(id, Shapes::C(Circle { cx, cy, r, color: ColorU8::from_rgba(rgba.red, rgba.green, rgba.blue, rgba.alpha), style }))
- }
- _ => {
- //
- }
- }
+ Self::recursive_rasterization_node_to_pixmap(item, pixmap);
+ }
+ }
+
+ #[wasm_bindgen(js_name = setAttrBySerde)]
+ pub fn set_attr_by_serde(&mut self, id: usize, value: JsValue) {
+ let message: WASMShape = serde_wasm_bindgen::from_value(value).unwrap();
+ match message.attr {
+ WASMShapesAttr::R(WASMRectAttr{ width, height, x, y , color, style}) => {
+ let color = parse_color(color);
+ let style = parse_style(style);
+ self.0.set_shape_to_child(id, Shapes::R(Rect { x, y, width, height, color, style }))
+ },
+ WASMShapesAttr::C(WASMCircleAttr{ cx, cy, r, color, style }) => {
+ let color = parse_color(color);
+ let style = parse_style(style);
+ self.0.set_shape_to_child(id, Shapes::C(Circle { cx, cy, r, color, style }))
},
WASMShapesAttr::RR(WASMRoundRectAttr{ width, height, r, x, y , color, style}) => {
+ let color = parse_color(color);
+ let style = parse_style(style);
- let mut parser_input = ParserInput::new(&color);
- let mut parser = Parser::new(&mut parser_input);
- let color = CSSColor::parse(&mut parser);
-
- match color {
- Ok(CSSColor::RGBA(rgba)) => {
- drop(parser_input);
- let style = match style.as_str() {
- "stroke" => {
- PaintStyle::Stroke
- },
- "fill" => {
- PaintStyle::Fill
- },
- _ => {
- PaintStyle::Stroke
- }
- };
- self.0.set_shape_to_child(id, Shapes::RR(RoundRect { x, y, r, width, height, color: ColorU8::from_rgba(rgba.red, rgba.green, rgba.blue, rgba.alpha), style }))
- }
- _ => {
- //
- }
- }
-
+ self.0.set_shape_to_child(id, Shapes::RR(RoundRect { x, y, r, width, height, color, style }))
},
WASMShapesAttr::L(WASMLineAttr{ p1, p2, stroke_width, color}) => {
-
- let mut parser_input = ParserInput::new(&color);
- let mut parser = Parser::new(&mut parser_input);
- let color = CSSColor::parse(&mut parser);
-
- match color {
- Ok(CSSColor::RGBA(rgba)) => {
- drop(parser_input);
- self.0.set_shape_to_child(id, Shapes::L(Line { p1, p2, stroke_width, color: ColorU8::from_rgba(rgba.red, rgba.green, rgba.blue, rgba.alpha) }))
- }
- _ => {
- //
- }
- }
-
+ let color = parse_color(color);
+ self.0.set_shape_to_child(id, Shapes::L(Line { p1, p2, stroke_width, color }))
},
WASMShapesAttr::P(WASMPointsAttr{ points , color, stroke_width, style }) => {
-
- let mut parser_input = ParserInput::new(&color);
- let mut parser = Parser::new(&mut parser_input);
- let color = CSSColor::parse(&mut parser);
-
- match color {
- Ok(CSSColor::RGBA(rgba)) => {
- drop(parser_input);
- let style = match style.as_str() {
- "stroke" => {
- PaintStyle::Stroke
- },
- "fill" => {
- PaintStyle::Fill
- },
- _ => {
- PaintStyle::Stroke
- }
- };
- self.0.set_shape_to_child(id, Shapes::P(Points { points, stroke_width, color: ColorU8::from_rgba(rgba.red, rgba.green, rgba.blue, rgba.alpha), style }))
- }
- _ => {
- //
- }
- }
+ let color = parse_color(color);
+ let style = parse_style(style);
+ self.0.set_shape_to_child(id, Shapes::P(Points { points, stroke_width, color, style }))
+ },
+ WASMShapesAttr::G(WASMGroupAttr {
+ x,
+ y,
+ color,
+ stroke_width,
+ style,
+ invert_clip,
+ }) => {
+ let color = parse_color(color);
+ let style = parse_style(style);
+ let mut clip = GroupClip::default();
+ clip.invert = invert_clip;
+
+ self.0.set_provider_to_child(
+ id,
+ Providers::G(Group {
+ x,
+ y,
+ color,
+ style,
+ stroke_width,
+ clip: Some(clip),
+ context: None,
+ }),
+ )
},
+ WASMShapesAttr::GC(WASMGroupClipAttr { clip }) => {
+ let provider = self
+ .0
+ .get_tree_node_by_id(id)
+ .unwrap()
+ .provider
+ .as_mut()
+ .unwrap();
+
+ match provider {
+ Providers::G(ref mut group) => group.set_clip_id(clip),
+ }
+ }
};
}
}
+
+fn parse_color(color: Option) -> Option {
+ if let Some(color_str) = color {
+ let mut parser_input = ParserInput::new(&color_str);
+ let mut parser = Parser::new(&mut parser_input);
+
+ if let Ok(css_color) = CSSColor::parse(&mut parser) {
+ if let CSSColor::RGBA(rgba) = css_color {
+ return Some(ColorU8::from_rgba(
+ rgba.red, rgba.green, rgba.blue, rgba.alpha,
+ ));
+ }
+ }
+ }
+ None
+}
+
+fn parse_style(style: Option) -> Option {
+ match style.as_deref() {
+ Some("stroke") => Some(PaintStyle::Stroke),
+ Some("fill") => Some(PaintStyle::Fill),
+ _ => None,
+ }
+}
\ No newline at end of file
diff --git a/soft-skia/src/instance.rs b/soft-skia/src/instance.rs
index 8506fa5..fa4df1e 100644
--- a/soft-skia/src/instance.rs
+++ b/soft-skia/src/instance.rs
@@ -1,5 +1,6 @@
use std::collections::HashMap;
+use crate::provider::Providers;
use crate::tree::Tree;
use crate::tree::Node;
use crate::shape::Shapes;
@@ -41,6 +42,11 @@ impl Instance {
child.shape = shape;
}
+ pub fn set_provider_to_child(&mut self, child_id: usize, provider: Providers) {
+ let mut child = self.get_tree_node_by_id(child_id).unwrap();
+ child.provider = Some(provider);
+ }
+
pub fn remove_child_from_container(&mut self, child_id: usize, container_id: usize) {
let container = self.get_tree_node_by_id(container_id).unwrap();
container.remove_child_by_id(child_id);
@@ -116,7 +122,7 @@ mod test {
}
}
- instance.set_shape_to_child(1, Shapes::R(Rect { x: 20, y: 20, width: 200, height: 200, color: ColorU8::from_rgba(0, 100, 0, 255), style: PaintStyle::Fill }));
+ instance.set_shape_to_child(1, Shapes::R(Rect { x: 20, y: 20, width: 200, height: 200, color: Some(ColorU8::from_rgba(0, 100, 0, 255)), style: None }));
match instance.get_tree_node_by_id(1).unwrap().shape {
Shapes::R(Rect { x, y, width, height, color, style }) => {
@@ -124,7 +130,7 @@ mod test {
assert_eq!(y, 20);
assert_eq!(width, 200);
assert_eq!(height, 200);
- assert_eq!(color.green(), 100);
+ assert_eq!(color.unwrap().green(), 100);
},
_ => {
panic!()
diff --git a/soft-skia/src/lib.rs b/soft-skia/src/lib.rs
index d697f26..f4e3d10 100644
--- a/soft-skia/src/lib.rs
+++ b/soft-skia/src/lib.rs
@@ -1,3 +1,4 @@
pub mod tree;
pub mod shape;
-pub mod instance;
\ No newline at end of file
+pub mod instance;
+pub mod provider;
\ No newline at end of file
diff --git a/soft-skia/src/provider.rs b/soft-skia/src/provider.rs
new file mode 100644
index 0000000..a790679
--- /dev/null
+++ b/soft-skia/src/provider.rs
@@ -0,0 +1,135 @@
+use std::collections::HashMap;
+
+use tiny_skia::{ColorU8, FillRule, Pixmap, Transform};
+pub use tiny_skia::{Mask, Path};
+
+use crate::shape::{DrawContext, PaintStyle};
+
+#[derive(Debug)]
+pub enum Providers {
+ G(Group),
+}
+
+pub trait Provider {
+ fn default() -> Self;
+ fn get_context(&self) -> Option<&DrawContext>;
+ fn set_context(&mut self, pixmap: &mut Pixmap, parent: Option<&Providers>) -> ();
+}
+
+impl Providers {
+ pub fn get_context(&self) -> Option<&DrawContext> {
+ match self {
+ Providers::G(group) => group.get_context(),
+ }
+ }
+ pub fn set_context(&mut self, pixmap: &mut Pixmap, parent: Option<&Providers>) -> () {
+ match self {
+ Providers::G(group) => group.set_context(pixmap, parent),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct GroupClip {
+ pub id: Option,
+ pub invert: Option,
+}
+
+impl GroupClip {
+ pub fn default() -> Self {
+ GroupClip {
+ id: None,
+ invert: None,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct Group {
+ pub x: Option,
+ pub y: Option,
+ pub color: Option,
+ pub style: Option,
+ pub stroke_width: Option,
+ pub clip: Option,
+ pub context: Option,
+}
+
+impl Provider for Group {
+ fn default() -> Self {
+ Group {
+ x: None,
+ y: None,
+ color: None,
+ style: None,
+ stroke_width: None,
+ clip: None,
+ context: None,
+ }
+ }
+
+ fn get_context(&self) -> Option<&DrawContext> {
+ return self.context.as_ref();
+ }
+
+ fn set_context(&mut self, _pixmap: &mut Pixmap, parent: Option<&Providers>) {
+ let mut inactive_nodes_map = HashMap::new();
+
+ if let Some(clip_id) = self.clip.as_ref().and_then(|c| c.id) {
+ inactive_nodes_map.insert(clip_id, true);
+ }
+
+ let mut context = DrawContext {
+ offset_x: self.x.unwrap_or(0),
+ offset_y: self.y.unwrap_or(0),
+ color: self.color,
+ style: self.style,
+ stroke_width: self.stroke_width,
+ mask: None,
+ inactive_nodes_map: Some(inactive_nodes_map),
+ };
+
+ match parent {
+ Some(Providers::G(group)) => {
+ if let Some(parent_context) = group.context.as_ref() {
+ context.offset_x = parent_context.offset_x + context.offset_x;
+ context.offset_y = parent_context.offset_y + context.offset_y;
+ context.color = context.color.or(parent_context.color);
+ context.style = context.style.or(parent_context.style);
+ context.stroke_width = context.stroke_width.or(parent_context.stroke_width);
+ }
+ }
+ _ => {}
+ };
+
+ self.context = Some(context);
+ }
+}
+
+impl Group {
+ pub fn set_clip_id(&mut self, id: usize) {
+ if self.clip.is_none() {
+ self.clip = Some(GroupClip::default());
+ }
+
+ self.clip.as_mut().unwrap().id = Some(id);
+ }
+
+ pub fn set_context_mask(&mut self, pixmap: &mut Pixmap, path: &Path) {
+ let mut mask: Option = None;
+
+ if let Some(GroupClip { invert, .. }) = &self.clip {
+ let mut clip_mask = Mask::new(pixmap.width(), pixmap.height()).unwrap();
+
+ clip_mask.fill_path(path, FillRule::Winding, true, Transform::default());
+
+ if let Some(true) = invert {
+ clip_mask.invert();
+ }
+
+ mask = Some(clip_mask);
+ }
+
+ self.context.as_mut().unwrap().mask = mask;
+ }
+}
diff --git a/soft-skia/src/shape.rs b/soft-skia/src/shape.rs
index fa810b6..cb83810 100644
--- a/soft-skia/src/shape.rs
+++ b/soft-skia/src/shape.rs
@@ -1,5 +1,7 @@
-pub use tiny_skia::{ColorU8, Paint, PathBuilder, Pixmap, Stroke, Transform, FillRule};
-use tiny_skia::{LineCap, LineJoin};
+use std::collections::HashMap;
+
+pub use tiny_skia::{ColorU8, FillRule, Mask, Paint, PathBuilder, Pixmap, Stroke, Transform};
+use tiny_skia::{LineCap, LineJoin, Path};
#[derive(Debug)]
pub enum Shapes {
@@ -10,15 +12,41 @@ pub enum Shapes {
P(Points),
}
+#[derive(Debug)]
+pub struct DrawContext {
+ pub offset_x: u32,
+ pub offset_y: u32,
+ pub color: Option,
+ pub style: Option,
+ pub stroke_width: Option,
+ pub mask: Option,
+ pub inactive_nodes_map: Option>,
+}
+
+impl DrawContext {
+ pub fn default() -> Self {
+ DrawContext {
+ offset_x: 0,
+ offset_y: 0,
+ color: None,
+ style: None,
+ stroke_width: None,
+ mask: None,
+ inactive_nodes_map: None,
+ }
+ }
+}
+
pub trait Shape {
fn default() -> Self;
- fn draw(&self, pixmap: &mut Pixmap) -> ();
+ fn draw(&self, pixmap: &mut Pixmap, context: &DrawContext) -> ();
+ fn get_path(&self, context: &DrawContext) -> Path;
}
#[derive(Debug, Clone, Copy)]
pub enum PaintStyle {
Stroke,
- Fill
+ Fill,
}
#[derive(Debug)]
@@ -27,8 +55,8 @@ pub struct Rect {
pub y: u32,
pub width: u32,
pub height: u32,
- pub color: ColorU8,
- pub style: PaintStyle,
+ pub color: Option,
+ pub style: Option,
}
#[derive(Debug)]
@@ -36,8 +64,8 @@ pub struct Circle {
pub cx: u32,
pub cy: u32,
pub r: u32,
- pub color: ColorU8,
- pub style: PaintStyle,
+ pub color: Option,
+ pub style: Option,
}
#[derive(Debug)]
@@ -47,62 +75,90 @@ pub struct RoundRect {
pub width: u32,
pub height: u32,
pub r: u32,
- pub color: ColorU8,
- pub style: PaintStyle,
+ pub color: Option,
+ pub style: Option,
}
#[derive(Debug)]
pub struct Line {
pub p1: [u32; 2],
pub p2: [u32; 2],
- pub color: ColorU8,
- pub stroke_width: u32
+ pub color: Option,
+ pub stroke_width: Option,
}
#[derive(Debug)]
pub struct Points {
pub points: Vec<[u32; 2]>,
- pub color: ColorU8,
- pub stroke_width: u32,
- pub style: PaintStyle,
+ pub color: Option,
+ pub stroke_width: Option,
+ pub style: Option,
}
impl Shapes {
- pub fn draw(&self, pixmap: &mut Pixmap) -> () {
+ pub fn draw(&self, pixmap: &mut Pixmap, context: &DrawContext) -> () {
+ match self {
+ Shapes::R(rect) => rect.draw(pixmap, context),
+ Shapes::C(circle) => circle.draw(pixmap, context),
+ Shapes::RR(round_rect) => round_rect.draw(pixmap, context),
+ Shapes::L(line) => line.draw(pixmap, context),
+ Shapes::P(points) => points.draw(pixmap, context),
+ }
+ }
+
+ pub fn get_path(&self, context: &DrawContext) -> Path {
match self {
- Shapes::R(rect) => rect.draw(pixmap),
- Shapes::C(circle) => circle.draw(pixmap),
- Shapes::RR(round_rect) => round_rect.draw(pixmap),
- Shapes::L(line) => line.draw(pixmap),
- Shapes::P(points) => points.draw(pixmap),
+ Shapes::R(rect) => rect.get_path(context),
+ Shapes::C(circle) => circle.get_path(context),
+ Shapes::RR(round_rect) => round_rect.get_path(context),
+ Shapes::L(line) => line.get_path(context),
+ Shapes::P(points) => points.get_path(context),
}
}
}
impl Shape for Rect {
fn default() -> Self {
- Rect { x: 0, y: 0, width: 0, height: 0, color: ColorU8::from_rgba(0, 0, 0, 255), style: PaintStyle::Fill }
+ Rect {
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0,
+ color: None,
+ style: None,
+ }
}
- fn draw(&self, pixmap: &mut Pixmap) -> () {
- let mut pb = PathBuilder::new();
- let mut paint = Paint::default();
+ fn draw(&self, pixmap: &mut Pixmap, context: &DrawContext) -> () {
+ let DrawContext {
+ color,
+ style,
+ stroke_width,
+ mask,
+ ..
+ } = context;
- pb.move_to(self.x as f32, self.y as f32);
- pb.line_to((self.x + self.width) as f32, self.y as f32);
- pb.line_to((self.x + self.width) as f32, (self.y + self.height) as f32);
- pb.line_to(self.x as f32, (self.y + self.height) as f32);
- pb.line_to(self.x as f32, self.y as f32);
- pb.close();
+ let path = self.get_path(context);
+
+ let mut paint = Paint::default();
+ let color = self
+ .color
+ .unwrap_or(color.unwrap_or(ColorU8::from_rgba(0, 0, 0, 255)));
+ let style = self.style.unwrap_or(style.unwrap_or(PaintStyle::Fill));
+ let mask = mask.as_ref();
- let path = pb.finish().unwrap();
- paint.set_color_rgba8(self.color.red(), self.color.green(), self.color.blue(), self.color.alpha());
+ paint.set_color_rgba8(color.red(), color.green(), color.blue(), color.alpha());
- match self.style {
+ match style {
PaintStyle::Stroke => {
- let stroke = Stroke::default();
- pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
- },
+ let mut stroke = Stroke::default();
+
+ if let &Some(w) = stroke_width {
+ stroke.width = w as f32
+ }
+
+ pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), mask);
+ }
PaintStyle::Fill => {
paint.anti_alias = true;
pixmap.fill_path(
@@ -110,13 +166,31 @@ impl Shape for Rect {
&paint,
FillRule::Winding,
Transform::identity(),
- None,
+ mask,
);
-
- },
+ }
_ => {}
}
+ }
+ fn get_path(
+ &self,
+ DrawContext {
+ offset_x, offset_y, ..
+ }: &DrawContext,
+ ) -> Path {
+ let mut pb = PathBuilder::new();
+ let x = self.x + offset_x;
+ let y = self.y + offset_y;
+
+ pb.move_to(x as f32, y as f32);
+ pb.line_to((x + self.width) as f32, y as f32);
+ pb.line_to((x + self.width) as f32, (y + self.height) as f32);
+ pb.line_to(x as f32, (y + self.height) as f32);
+ pb.line_to(x as f32, y as f32);
+ pb.close();
+
+ pb.finish().unwrap()
}
}
@@ -125,23 +199,37 @@ impl Shape for Circle {
todo!()
}
- fn draw(&self, pixmap: &mut Pixmap) -> () {
+ fn draw(&self, pixmap: &mut Pixmap, context: &DrawContext) -> () {
+ let DrawContext {
+ color,
+ style,
+ stroke_width,
+ mask,
+ ..
+ } = context;
+
+ let path = self.get_path(context);
+
let mut paint = Paint::default();
- let mut pb = PathBuilder::new();
+ let color = self
+ .color
+ .unwrap_or(color.unwrap_or(ColorU8::from_rgba(0, 0, 0, 255)));
+ let style = self.style.unwrap_or(style.unwrap_or(PaintStyle::Fill));
+ let mask = mask.as_ref();
- paint.set_color_rgba8(self.color.red(), self.color.green(), self.color.blue(), self.color.alpha());
+ paint.set_color_rgba8(color.red(), color.green(), color.blue(), color.alpha());
paint.anti_alias = true;
- pb.push_circle(self.cx as f32, self.cy as f32, self.r as f32);
- pb.close();
+ match style {
+ PaintStyle::Stroke => {
+ let mut stroke = Stroke::default();
- let path = pb.finish().unwrap();
+ if let &Some(w) = stroke_width {
+ stroke.width = w as f32
+ }
- match self.style {
- PaintStyle::Stroke => {
- let stroke = Stroke::default();
- pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
- },
+ pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), mask);
+ }
PaintStyle::Fill => {
paint.anti_alias = true;
pixmap.fill_path(
@@ -149,13 +237,28 @@ impl Shape for Circle {
&paint,
FillRule::Winding,
Transform::identity(),
- None,
+ mask,
);
-
- },
+ }
_ => {}
}
}
+
+ fn get_path(
+ &self,
+ DrawContext {
+ offset_x, offset_y, ..
+ }: &DrawContext,
+ ) -> Path {
+ let mut pb: PathBuilder = PathBuilder::new();
+ let x = self.cx + offset_x;
+ let y = self.cy + offset_y;
+
+ pb.push_circle(x as f32, y as f32, self.r as f32);
+ pb.close();
+
+ pb.finish().unwrap()
+ }
}
impl Shape for RoundRect {
@@ -163,31 +266,37 @@ impl Shape for RoundRect {
todo!()
}
- fn draw(&self, pixmap: &mut Pixmap) -> () {
+ fn draw(&self, pixmap: &mut Pixmap, context: &DrawContext) -> () {
+ let DrawContext {
+ color,
+ style,
+ stroke_width,
+ mask,
+ ..
+ } = context;
+
+ let path = self.get_path(context);
+
let mut paint = Paint::default();
- let mut pb = PathBuilder::new();
+ let color = self
+ .color
+ .unwrap_or(color.unwrap_or(ColorU8::from_rgba(0, 0, 0, 255)));
+ let style = self.style.unwrap_or(style.unwrap_or(PaintStyle::Fill));
+ let mask = mask.as_ref();
- paint.set_color_rgba8(self.color.red(), self.color.green(), self.color.blue(), self.color.alpha());
+ paint.set_color_rgba8(color.red(), color.green(), color.blue(), color.alpha());
paint.anti_alias = true;
- pb.move_to((self.x + self.r) as f32, self.y as f32);
- pb.line_to((self.x + self.width - self.r) as f32, self.y as f32);
- pb.quad_to((self.x + self.width) as f32, self.y as f32, (self.x + self.width) as f32, (self.y + self.r) as f32);
- pb.line_to((self.x + self.width) as f32, (self.y + self.height - self.r) as f32);
- pb.quad_to((self.x + self.width) as f32, (self.y + self.height) as f32, (self.x + self.width - self.r) as f32, (self.y + self.height) as f32);
- pb.line_to((self.x + self.r) as f32, (self.y + self.height) as f32);
- pb.quad_to(self.x as f32, (self.y + self.height) as f32, self.x as f32, (self.y + self.height - self.r) as f32);
- pb.line_to(self.x as f32, (self.y + self.r) as f32);
- pb.quad_to(self.x as f32, self.y as f32, (self.x + self.r) as f32, self.y as f32);
- pb.close();
+ match style {
+ PaintStyle::Stroke => {
+ let mut stroke = Stroke::default();
- let path = pb.finish().unwrap();
+ if let &Some(w) = stroke_width {
+ stroke.width = w as f32
+ }
- match self.style {
- PaintStyle::Stroke => {
- let stroke = Stroke::default();
- pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
- },
+ pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), mask);
+ }
PaintStyle::Fill => {
paint.anti_alias = true;
pixmap.fill_path(
@@ -195,40 +304,102 @@ impl Shape for RoundRect {
&paint,
FillRule::Winding,
Transform::identity(),
- None,
+ mask,
);
-
- },
+ }
_ => {}
}
}
-}
+ fn get_path(
+ &self,
+ DrawContext {
+ offset_x, offset_y, ..
+ }: &DrawContext,
+ ) -> Path {
+ let mut pb = PathBuilder::new();
+ let x = self.x + offset_x;
+ let y = self.y + offset_y;
+
+ pb.move_to((x + self.r) as f32, y as f32);
+ pb.line_to((x + self.width - self.r) as f32, y as f32);
+ pb.quad_to(
+ (x + self.width) as f32,
+ y as f32,
+ (x + self.width) as f32,
+ (y + self.r) as f32,
+ );
+ pb.line_to((x + self.width) as f32, (y + self.height - self.r) as f32);
+ pb.quad_to(
+ (x + self.width) as f32,
+ (y + self.height) as f32,
+ (x + self.width - self.r) as f32,
+ (y + self.height) as f32,
+ );
+ pb.line_to((x + self.r) as f32, (y + self.height) as f32);
+ pb.quad_to(
+ x as f32,
+ (y + self.height) as f32,
+ x as f32,
+ (y + self.height - self.r) as f32,
+ );
+ pb.line_to(x as f32, (y + self.r) as f32);
+ pb.quad_to(x as f32, y as f32, (x + self.r) as f32, y as f32);
+ pb.close();
+
+ pb.finish().unwrap()
+ }
+}
impl Shape for Line {
fn default() -> Self {
todo!()
}
- fn draw(&self, pixmap: &mut Pixmap) -> () {
- let mut pb = PathBuilder::new();
- let mut paint = Paint::default();
+ fn draw(&self, pixmap: &mut Pixmap, context: &DrawContext) -> () {
+ let DrawContext {
+ color,
+ stroke_width,
+ mask,
+ ..
+ } = context;
- pb.move_to(self.p1[0] as f32, self.p1[1] as f32);
- pb.line_to(self.p2[0] as f32, self.p2[1] as f32);
- pb.close();
+ let path = self.get_path(context);
+
+ let mut paint = Paint::default();
+ let color = self
+ .color
+ .unwrap_or(color.unwrap_or(ColorU8::from_rgba(0, 0, 0, 255)));
+ let stroke_width = self.stroke_width.unwrap_or(stroke_width.unwrap_or(1));
+ let mask = mask.as_ref();
- let path = pb.finish().unwrap();
let stroke = Stroke {
- width: self.stroke_width as f32,
+ width: stroke_width as f32,
miter_limit: 4.0,
line_cap: LineCap::Butt,
line_join: LineJoin::Miter,
dash: None,
};
- paint.set_color_rgba8(self.color.red(), self.color.green(), self.color.blue(), self.color.alpha());
- pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
+ paint.set_color_rgba8(color.red(), color.green(), color.blue(), color.alpha());
+ pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), mask);
+ }
+
+ fn get_path(
+ &self,
+ DrawContext {
+ offset_x, offset_y, ..
+ }: &DrawContext,
+ ) -> Path {
+ let mut pb = PathBuilder::new();
+ let p1 = [self.p1[0] + offset_x, self.p1[1] + offset_y];
+ let p2 = [self.p2[0] + offset_x, self.p2[1] + offset_y];
+
+ pb.move_to(p1[0] as f32, p1[1] as f32);
+ pb.line_to(p2[0] as f32, p2[1] as f32);
+ pb.close();
+
+ pb.finish().unwrap()
}
}
@@ -237,30 +408,38 @@ impl Shape for Points {
todo!()
}
- fn draw(&self, pixmap: &mut Pixmap) -> () {
- let mut pb = PathBuilder::new();
- let mut paint = Paint::default();
- paint.set_color_rgba8(self.color.red(), self.color.green(), self.color.blue(), self.color.alpha());
+ fn draw(&self, pixmap: &mut Pixmap, context: &DrawContext) -> () {
+ let DrawContext {
+ color,
+ style,
+ stroke_width,
+ mask,
+ ..
+ } = context;
- pb.move_to(self.points[0][0] as f32, self.points[0][1] as f32);
- for i in 1..self.points.len() {
- pb.line_to(self.points[i][0] as f32, self.points[i][1] as f32);
- }
- pb.close();
+ let path = self.get_path(context);
+
+ let mut paint = Paint::default();
+ let color = self
+ .color
+ .unwrap_or(color.unwrap_or(ColorU8::from_rgba(0, 0, 0, 255)));
+ let style = self.style.unwrap_or(style.unwrap_or(PaintStyle::Fill));
+ let stroke_width = self.stroke_width.unwrap_or(stroke_width.unwrap_or(1));
+ let mask = mask.as_ref();
- let path = pb.finish().unwrap();
+ paint.set_color_rgba8(color.red(), color.green(), color.blue(), color.alpha());
- match self.style {
+ match style {
PaintStyle::Stroke => {
let stroke = Stroke {
- width: self.stroke_width as f32,
+ width: stroke_width as f32,
miter_limit: 4.0,
line_cap: LineCap::Butt,
line_join: LineJoin::Miter,
dash: None,
};
- pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);
- },
+ pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), mask);
+ }
PaintStyle::Fill => {
paint.anti_alias = true;
pixmap.fill_path(
@@ -268,36 +447,74 @@ impl Shape for Points {
&paint,
FillRule::Winding,
Transform::identity(),
- None,
+ mask,
);
-
- },
+ }
_ => {}
}
}
+
+ fn get_path(
+ &self,
+ DrawContext {
+ offset_x, offset_y, ..
+ }: &DrawContext,
+ ) -> Path {
+ let mut pb = PathBuilder::new();
+
+ pb.move_to(
+ (self.points[0][0] + offset_x) as f32,
+ (self.points[0][1] + offset_y) as f32,
+ );
+ for i in 1..self.points.len() {
+ pb.line_to(
+ (self.points[i][0] + offset_x) as f32,
+ (self.points[i][1] + offset_y) as f32,
+ );
+ }
+
+ pb.close();
+
+ pb.finish().unwrap()
+ }
}
#[cfg(test)]
mod test {
- use crate::shape::Rect;
+ use super::{ColorU8, FillRule, Paint, PathBuilder, Pixmap, Stroke, Transform};
use crate::shape::Circle;
+ use crate::shape::DrawContext;
use crate::shape::PaintStyle;
+ use crate::shape::Rect;
use crate::shape::Shape;
- use super::{ColorU8, Paint, PathBuilder, Pixmap, Stroke, Transform, FillRule};
#[test]
fn test_draw_rect() {
let mut pixmap = Pixmap::new(400 as u32, 400 as u32).unwrap();
- let shape_0 = Rect { x: 20, y: 20, width: 200, height: 200, color: ColorU8::from_rgba(0, 255, 0, 200), style: PaintStyle::Fill };
- let shape_1 = Rect { x: 120, y: 80, width: 200, height: 200, color: ColorU8::from_rgba(0, 255, 0, 200), style: PaintStyle::Fill };
+ let shape_0 = Rect {
+ x: 20,
+ y: 20,
+ width: 200,
+ height: 200,
+ color: None,
+ style: None,
+ };
+ let shape_1 = Rect {
+ x: 120,
+ y: 80,
+ width: 200,
+ height: 200,
+ color: None,
+ style: None,
+ };
- shape_0.draw(&mut pixmap);
- shape_1.draw(&mut pixmap);
+ shape_0.draw(&mut pixmap, &DrawContext::default());
+ shape_1.draw(&mut pixmap, &DrawContext::default());
let data = pixmap.clone().encode_png().unwrap();
let data_url = base64::encode(&data);
println!("{}", format!("data:image/png;base64,{}", data_url));
}
-}
\ No newline at end of file
+}
diff --git a/soft-skia/src/tree.rs b/soft-skia/src/tree.rs
index 575b9e7..70ef3c9 100644
--- a/soft-skia/src/tree.rs
+++ b/soft-skia/src/tree.rs
@@ -1,27 +1,36 @@
-use std::slice::Iter;
-use std::slice::IterMut;
-use tiny_skia::{ColorU8};
-use crate::shape::Rect;
+use crate::provider::Providers;
use crate::shape::Circle;
+use crate::shape::Rect;
use crate::shape::Shape;
+use crate::shape::DrawContext;
use crate::shape::Shapes;
+use std::slice::Iter;
+use std::slice::IterMut;
+use tiny_skia::ColorU8;
+use tiny_skia::Pixmap;
#[derive(Debug)]
pub struct Tree {
- root: Box
+ root: Box,
}
#[derive(Debug)]
pub struct Node {
pub id: usize,
pub shape: Shapes,
- pub children: Vec>
+ pub provider: Option,
+ pub children: Vec>,
}
impl Tree {
pub fn default(id: usize) -> Self {
Tree {
- root: Box::new(Node { id, shape: Shapes::R(Rect::default()), children: Vec::new() })
+ root: Box::new(Node {
+ id,
+ shape: Shapes::R(Rect::default()),
+ children: Vec::new(),
+ provider: None,
+ }),
}
}
@@ -32,21 +41,33 @@ impl Tree {
impl Node {
pub fn default(id: usize, shape: Shapes) -> Self {
- Node { id, shape, children: Vec::new() }
+ Node {
+ id,
+ shape,
+ children: Vec::new(),
+ provider: None,
+ }
+ }
+
+ pub fn draw(&self, pixmap: &mut Pixmap, context: Option<&DrawContext>) {
+ self.shape
+ .draw(pixmap, context.unwrap_or(&DrawContext::default()));
}
pub fn append_node(&mut self, node: Node) {
- self.children.push(Box::new(node));
+ self.append_boxed_node(Box::new(node));
}
pub fn insert_node_before_id(&mut self, before_id: usize, node: Node) {
- let before_index = self.children.iter().position(|t| t.id == before_id).unwrap();
- self.children.insert(before_index, Box::new(node));
+ self.insert_boxed_node_before_id(before_id, Box::new(node));
}
pub fn insert_boxed_node_before_id(&mut self, before_id: usize, boxed_node: Box) {
- let before_index = self.children.iter().position(|t| t.id == before_id).unwrap();
- self.children.insert(before_index, boxed_node);
+ if let Some(before_index) = self.children.iter().position(|t| t.id == before_id) {
+ self.children.insert(before_index, boxed_node);
+ } else {
+ self.append_boxed_node(boxed_node)
+ }
}
pub fn append_boxed_node(&mut self, boxed_node: Box) {
@@ -86,12 +107,12 @@ impl Node {
mod test {
use crate::shape::PaintStyle;
- use super::Tree;
- use super::Node;
- use super::Shapes;
- use super::Rect;
use super::Circle;
use super::ColorU8;
+ use super::Node;
+ use super::Rect;
+ use super::Shapes;
+ use super::Tree;
#[test]
fn test_tree() {
@@ -105,21 +126,42 @@ mod test {
assert_eq!(root.id, 10086);
match root.shape {
- Shapes::R(Rect { x, y, width, height, color, style }) => {
+ Shapes::R(Rect {
+ x,
+ y,
+ width,
+ height,
+ color,
+ style,
+ }) => {
assert_eq!(width, 0);
assert_eq!(height, 0);
- },
+ }
_ => {
panic!()
}
}
- root.shape = Shapes::R(Rect { x: 0, y: 0, width: 400, height: 400, color: ColorU8::from_rgba(0, 0, 0, 255), style: PaintStyle::Fill });
+ root.shape = Shapes::R(Rect {
+ x: 0,
+ y: 0,
+ width: 400,
+ height: 400,
+ color: None,
+ style: None,
+ });
match root.shape {
- Shapes::R(Rect { x, y, width, height, color, style: PaintStyle::Fill }) => {
+ Shapes::R(Rect {
+ x,
+ y,
+ width,
+ height,
+ color,
+ style: None,
+ }) => {
assert_eq!(width, 400);
assert_eq!(height, 400);
- },
+ }
_ => {
panic!()
}
@@ -128,19 +170,75 @@ mod test {
#[test]
fn test_node() {
- let mut node = Node { id: 0, shape: Shapes::R(Rect { x: 0, y: 0, width: 400, height: 400, color: ColorU8::from_rgba(0, 0, 0, 255), style: PaintStyle::Fill }), children: Vec::new() };
+ let mut node = Node {
+ id: 0,
+ shape: Shapes::R(Rect {
+ x: 0,
+ y: 0,
+ width: 400,
+ height: 400,
+ color: None,
+ style: None,
+ }),
+ children: Vec::new(),
+ provider: None,
+ };
assert_eq!(node.id, 0);
assert_eq!(node.get_children_len(), 0);
- node.append_node(Node { id: 1, shape: Shapes::C(Circle { cx: 100, cy: 100, r: 50, color: ColorU8::from_rgba(0, 0, 0, 255), style: PaintStyle::Fill }), children: Vec::new() });
+ node.append_node(Node {
+ id: 1,
+ shape: Shapes::C(Circle {
+ cx: 100,
+ cy: 100,
+ r: 50,
+ color: None,
+ style: None,
+ }),
+ children: Vec::new(),
+ provider: None,
+ });
assert_eq!(node.get_children_len(), 1);
- node.append_node(Node { id: 2, shape: Shapes::C(Circle { cx: 100, cy: 100, r: 50, color: ColorU8::from_rgba(0, 0, 0, 255), style: PaintStyle::Fill }), children: Vec::new() });
+ node.append_node(Node {
+ id: 2,
+ shape: Shapes::C(Circle {
+ cx: 100,
+ cy: 100,
+ r: 50,
+ color: None,
+ style: None,
+ }),
+ children: Vec::new(),
+ provider: None,
+ });
assert_eq!(node.get_children_len(), 2);
- node.append_boxed_node(Box::new(Node { id: 3, shape: Shapes::C(Circle { cx: 100, cy: 100, r: 50, color: ColorU8::from_rgba(0, 0, 0, 255), style: PaintStyle::Fill }), children: Vec::new() }));
- node.append_boxed_node(Box::new(Node { id: 4, shape: Shapes::C(Circle { cx: 100, cy: 100, r: 50, color: ColorU8::from_rgba(0, 0, 0, 255), style: PaintStyle::Fill }), children: Vec::new() }));
+ node.append_boxed_node(Box::new(Node {
+ id: 3,
+ shape: Shapes::C(Circle {
+ cx: 100,
+ cy: 100,
+ r: 50,
+ color: None,
+ style: None,
+ }),
+ children: Vec::new(),
+ provider: None,
+ }));
+ node.append_boxed_node(Box::new(Node {
+ id: 4,
+ shape: Shapes::C(Circle {
+ cx: 100,
+ cy: 100,
+ r: 50,
+ color: None,
+ style: None,
+ }),
+ children: Vec::new(),
+ provider: None,
+ }));
assert_eq!(node.get_children_len(), 4);
let child_index_0 = node.get_child_by_index(0).unwrap();
@@ -162,13 +260,41 @@ mod test {
assert_eq!(node.children[0].id, 1);
assert_eq!(node.children[1].id, 4);
- node.insert_node_before_id(4, Node { id: 200, shape: Shapes::C(Circle { cx: 100, cy: 100, r: 50, color: ColorU8::from_rgba(0, 0, 0, 255), style: PaintStyle::Fill }), children: Vec::new() });
+ node.insert_node_before_id(
+ 4,
+ Node {
+ id: 200,
+ shape: Shapes::C(Circle {
+ cx: 100,
+ cy: 100,
+ r: 50,
+ color: None,
+ style: None,
+ }),
+ children: Vec::new(),
+ provider: None,
+ },
+ );
assert_eq!(node.get_children_len(), 3);
assert_eq!(node.children[0].id, 1);
assert_eq!(node.children[1].id, 200);
assert_eq!(node.children[2].id, 4);
- node.insert_node_before_id(4, Node { id: 300, shape: Shapes::C(Circle { cx: 100, cy: 100, r: 50, color: ColorU8::from_rgba(0, 0, 0, 255), style: PaintStyle::Fill }), children: Vec::new() });
+ node.insert_node_before_id(
+ 4,
+ Node {
+ id: 300,
+ shape: Shapes::C(Circle {
+ cx: 100,
+ cy: 100,
+ r: 50,
+ color: None,
+ style: None,
+ }),
+ children: Vec::new(),
+ provider: None,
+ },
+ );
assert_eq!(node.get_children_len(), 4);
assert_eq!(node.children[0].id, 1);
assert_eq!(node.children[1].id, 200);
@@ -177,22 +303,34 @@ mod test {
for item in node.children_iter_mut() {
match item.shape {
- Shapes::C(Circle { ref mut cx, cy, r, color, style }) => {
+ Shapes::C(Circle {
+ ref mut cx,
+ cy,
+ r,
+ color,
+ style,
+ }) => {
assert_eq!(*cx, 100);
*cx = 200;
assert_eq!(*cx, 200);
- },
+ }
_ => panic!(),
}
}
for item in node.children_iter() {
match item.shape {
- Shapes::C(Circle { cx, cy, r, color, style }) => {
+ Shapes::C(Circle {
+ cx,
+ cy,
+ r,
+ color,
+ style,
+ }) => {
assert_eq!(cx, 200);
- },
+ }
_ => panic!(),
}
}
}
-}
\ No newline at end of file
+}
diff --git a/vue-playground/src/App.vue b/vue-playground/src/App.vue
index 3b19475..6d13dc8 100644
--- a/vue-playground/src/App.vue
+++ b/vue-playground/src/App.vue
@@ -12,16 +12,26 @@
This super cool editor is based on vue-live !
-
You can edit this ->
-
void 0" />
+
+ You can edit
this
+
->
+
+ void 0"
+ @input="input"
+ />
@@ -50,7 +60,7 @@
-
+