mirror of
https://github.com/penpot/penpot.git
synced 2025-02-23 23:35:58 -05:00
Merge pull request #5824 from penpot/alotor-propagate-modifiers
✨ Use skia matrix for internal data
This commit is contained in:
commit
773b4fe02e
13 changed files with 228 additions and 211 deletions
|
@ -297,11 +297,15 @@
|
|||
(->> resize-events-stream
|
||||
(rx/take-until stopper)
|
||||
(rx/last)
|
||||
(rx/map #(dwm/set-modifiers (dwm/create-modif-tree ids %) (contains? layout :scale-text))))
|
||||
(rx/map #(dwm/apply-modifiers {:modifiers (dwm/create-modif-tree ids %)
|
||||
:ignore-constraints (contains? layout :scale-text)})))
|
||||
(rx/empty)))
|
||||
|
||||
(rx/of (dwm/apply-modifiers)
|
||||
(finish-transform))))))))
|
||||
(rx/of
|
||||
(if (features/active-feature? state "render-wasm/v1")
|
||||
(dwm/clear-local-transform)
|
||||
(dwm/apply-modifiers))
|
||||
(finish-transform))))))))
|
||||
|
||||
(defn trigger-bounding-box-cloaking
|
||||
"Trigger the bounding box cloaking (with default timer of 1sec)
|
||||
|
|
|
@ -249,7 +249,7 @@
|
|||
offset (:offset stop)]
|
||||
[r g b a (* 100 offset)]))
|
||||
stops)))))
|
||||
(h/call internal-module "_add_shape_fill_stops" stops-ptr n-stops))
|
||||
(h/call internal-module "_add_shape_fill_stops"))
|
||||
|
||||
(some? image)
|
||||
(let [id (dm/get-prop image :id)
|
||||
|
@ -423,6 +423,34 @@
|
|||
[opacity]
|
||||
(h/call internal-module "_set_shape_opacity" (or opacity 1)))
|
||||
|
||||
(defn- translate-constraint-h
|
||||
[type]
|
||||
(case type
|
||||
:left 0
|
||||
:right 1
|
||||
:leftright 2
|
||||
:center 3
|
||||
:scale 4))
|
||||
|
||||
(defn set-constraints-h
|
||||
[constraint]
|
||||
(when constraint
|
||||
(h/call internal-module "_set_shape_constraint_h" (translate-constraint-h constraint))))
|
||||
|
||||
(defn- translate-constraint-v
|
||||
[type]
|
||||
(case type
|
||||
:top 0
|
||||
:bottom 1
|
||||
:topbottom 2
|
||||
:center 3
|
||||
:scale 4))
|
||||
|
||||
(defn set-constraints-v
|
||||
[constraint]
|
||||
(when constraint
|
||||
(h/call internal-module "_set_shape_constraint_v" (translate-constraint-v constraint))))
|
||||
|
||||
(defn set-shape-hidden
|
||||
[hidden]
|
||||
(h/call internal-module "_set_shape_hidden" hidden))
|
||||
|
|
|
@ -129,6 +129,8 @@
|
|||
:blur (api/set-shape-blur v)
|
||||
:svg-attrs (when (= (:type self) :path)
|
||||
(api/set-shape-path-attrs v))
|
||||
:constraints-h (api/set-constraints-h v)
|
||||
:constraints-v (api/set-constraints-v v)
|
||||
:content (cond
|
||||
(= (:type self) :path)
|
||||
(api/set-shape-path-content v)
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
use std::collections::HashSet;
|
||||
use uuid::Uuid;
|
||||
|
||||
use skia_safe as skia;
|
||||
|
||||
mod debug;
|
||||
mod math;
|
||||
mod matrix;
|
||||
mod mem;
|
||||
mod render;
|
||||
mod shapes;
|
||||
|
@ -14,7 +10,7 @@ mod utils;
|
|||
mod view;
|
||||
|
||||
use crate::mem::SerializableResult;
|
||||
use crate::shapes::{BoolType, Kind, Path, TransformEntry};
|
||||
use crate::shapes::{BoolType, ConstraintH, ConstraintV, Kind, Path, TransformEntry};
|
||||
use crate::state::State;
|
||||
use crate::utils::uuid_from_u32_quartet;
|
||||
|
||||
|
@ -268,20 +264,22 @@ pub extern "C" fn add_shape_radial_fill(
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn add_shape_fill_stops(ptr: *mut shapes::RawStopData, n_stops: u32) {
|
||||
pub extern "C" fn add_shape_fill_stops() {
|
||||
let bytes = mem::bytes();
|
||||
|
||||
let entries: Vec<_> = bytes
|
||||
.chunks(size_of::<shapes::RawStopData>())
|
||||
.map(|data| shapes::RawStopData::from_bytes(data.try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer");
|
||||
|
||||
if let Some(shape) = state.current_shape() {
|
||||
let len = n_stops as usize;
|
||||
|
||||
unsafe {
|
||||
let buffer = Vec::<shapes::RawStopData>::from_raw_parts(ptr, len, len);
|
||||
shape
|
||||
.add_fill_gradient_stops(buffer)
|
||||
.expect("could not add gradient stops");
|
||||
mem::free_bytes();
|
||||
}
|
||||
shape
|
||||
.add_fill_gradient_stops(entries)
|
||||
.expect("could not add gradient stops");
|
||||
}
|
||||
mem::free_bytes();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -390,6 +388,22 @@ pub extern "C" fn set_shape_opacity(opacity: f32) {
|
|||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_shape_constraint_h(constraint: u8) {
|
||||
let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer");
|
||||
if let Some(shape) = state.current_shape() {
|
||||
shape.set_constraint_h(ConstraintH::from(constraint))
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_shape_constraint_v(constraint: u8) {
|
||||
let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer");
|
||||
if let Some(shape) = state.current_shape() {
|
||||
shape.set_constraint_v(ConstraintV::from(constraint))
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_shape_hidden(hidden: bool) {
|
||||
let state = unsafe { STATE.as_mut() }.expect("Got an invalid state pointer");
|
||||
|
@ -598,37 +612,14 @@ pub extern "C" fn set_shape_path_attrs(num_attrs: u32) {
|
|||
pub extern "C" fn propagate_modifiers() -> *mut u8 {
|
||||
let bytes = mem::bytes();
|
||||
|
||||
let mut entries: Vec<_> = bytes
|
||||
.chunks(size_of::<TransformEntry>())
|
||||
let entries: Vec<_> = bytes
|
||||
.chunks(size_of::<<TransformEntry as SerializableResult>::BytesType>())
|
||||
.map(|data| TransformEntry::from_bytes(data.try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
let state = unsafe { STATE.as_mut() }.expect("got an invalid state pointer");
|
||||
let result = shapes::propagate_modifiers(state, entries);
|
||||
|
||||
let mut processed = HashSet::<Uuid>::new();
|
||||
|
||||
let mut result = Vec::<TransformEntry>::new();
|
||||
|
||||
// Propagate the transform to children
|
||||
while let Some(entry) = entries.pop() {
|
||||
if !processed.contains(&entry.id) {
|
||||
if let Some(shape) = state.shapes.get(&entry.id) {
|
||||
let mut children: Vec<TransformEntry> = shape
|
||||
.children
|
||||
.iter()
|
||||
.map(|id| TransformEntry {
|
||||
id: id.clone(),
|
||||
transform: entry.transform,
|
||||
})
|
||||
.collect();
|
||||
|
||||
entries.append(&mut children);
|
||||
|
||||
processed.insert(entry.id);
|
||||
result.push(entry.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
mem::write_vec(result)
|
||||
}
|
||||
|
||||
|
@ -644,7 +635,7 @@ pub extern "C" fn set_modifiers() {
|
|||
let bytes = mem::bytes();
|
||||
|
||||
let entries: Vec<_> = bytes
|
||||
.chunks(size_of::<TransformEntry>())
|
||||
.chunks(size_of::<<TransformEntry as SerializableResult>::BytesType>())
|
||||
.map(|data| TransformEntry::from_bytes(data.try_into().unwrap()))
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
// allowing dead code so we can have some API's that are not used yet without warnings
|
||||
#![allow(dead_code)]
|
||||
use skia_safe as skia;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Matrix {
|
||||
pub a: f32,
|
||||
pub b: f32,
|
||||
pub c: f32,
|
||||
pub d: f32,
|
||||
pub e: f32,
|
||||
pub f: f32,
|
||||
}
|
||||
|
||||
impl Matrix {
|
||||
pub fn new(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> Self {
|
||||
Self { a, b, c, d, e, f }
|
||||
}
|
||||
|
||||
pub fn translate(x: f32, y: f32) -> Self {
|
||||
Self::new(0.0, 0.0, 0.0, 0.0, x, y)
|
||||
}
|
||||
|
||||
pub fn identity() -> Self {
|
||||
Self {
|
||||
a: 1.,
|
||||
b: 0.,
|
||||
c: 0.,
|
||||
d: 1.,
|
||||
e: 0.,
|
||||
f: 0.,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_skia_matrix(&self) -> skia::Matrix {
|
||||
let mut res = skia::Matrix::new_identity();
|
||||
|
||||
let (translate_x, translate_y) = self.translation();
|
||||
let (scale_x, scale_y) = self.scale();
|
||||
let (skew_x, skew_y) = self.skew();
|
||||
res.set_all(
|
||||
scale_x,
|
||||
skew_x,
|
||||
translate_x,
|
||||
skew_y,
|
||||
scale_y,
|
||||
translate_y,
|
||||
0.,
|
||||
0.,
|
||||
1.,
|
||||
);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn no_translation(&self) -> Self {
|
||||
let mut res = Self::identity();
|
||||
res.c = self.c;
|
||||
res.b = self.b;
|
||||
res.a = self.a;
|
||||
res.d = self.d;
|
||||
res
|
||||
}
|
||||
|
||||
fn translation(&self) -> (f32, f32) {
|
||||
(self.e, self.f)
|
||||
}
|
||||
|
||||
fn scale(&self) -> (f32, f32) {
|
||||
(self.a, self.d)
|
||||
}
|
||||
|
||||
fn skew(&self) -> (f32, f32) {
|
||||
(self.c, self.b)
|
||||
}
|
||||
|
||||
pub fn product(&self, other: &Matrix) -> Matrix {
|
||||
let a = self.a * other.a + self.c * other.b;
|
||||
let b = self.b * other.a + self.d * other.b;
|
||||
let c = self.a * other.c + self.c * other.d;
|
||||
let d = self.b * other.c + self.d * other.d;
|
||||
let e = self.a * other.e + self.c * other.f + self.e;
|
||||
let f = self.b * other.e + self.d * other.f + self.f;
|
||||
Matrix::new(a, b, c, d, e, f)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> [u8; 24] {
|
||||
let mut result = [0; 24];
|
||||
result[0..4].clone_from_slice(&self.a.to_le_bytes());
|
||||
result[4..8].clone_from_slice(&self.b.to_le_bytes());
|
||||
result[8..12].clone_from_slice(&self.c.to_le_bytes());
|
||||
result[12..16].clone_from_slice(&self.d.to_le_bytes());
|
||||
result[16..20].clone_from_slice(&self.e.to_le_bytes());
|
||||
result[20..24].clone_from_slice(&self.f.to_le_bytes());
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_product() {
|
||||
let a = Matrix::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
|
||||
let b = Matrix::new(6.0, 5.0, 4.0, 3.0, 2.0, 1.0);
|
||||
|
||||
assert_eq!(
|
||||
a.product(&b),
|
||||
Matrix::new(21.0, 32.0, 13.0, 20.0, 10.0, 14.0)
|
||||
);
|
||||
|
||||
let a = Matrix::new(7.0, 4.0, 8.0, 3.0, 9.0, 5.0);
|
||||
let b = Matrix::new(7.0, 4.0, 8.0, 3.0, 9.0, 5.0);
|
||||
|
||||
assert_eq!(
|
||||
a.product(&b),
|
||||
Matrix::new(81.0, 40.0, 80.0, 41.0, 112.0, 56.0)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -57,7 +57,7 @@ pub trait SerializableResult {
|
|||
by the implementation of SerializableResult trait
|
||||
*/
|
||||
pub fn write_vec<T: SerializableResult>(result: Vec<T>) -> *mut u8 {
|
||||
let elem_size = size_of::<T>();
|
||||
let elem_size = size_of::<T::BytesType>();
|
||||
let bytes_len = 4 + result.len() * elem_size;
|
||||
let mut result_bytes = Vec::<u8>::with_capacity(bytes_len);
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ use std::collections::HashMap;
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::math;
|
||||
use crate::matrix::Matrix;
|
||||
use crate::view::Viewbox;
|
||||
use skia::Matrix;
|
||||
|
||||
mod blend;
|
||||
mod cache;
|
||||
|
@ -240,18 +240,14 @@ impl RenderState {
|
|||
clip_bounds: Option<skia::Rect>,
|
||||
) {
|
||||
if let Some(modifiers) = modifiers {
|
||||
self.drawing_surface
|
||||
.canvas()
|
||||
.concat(&modifiers.to_skia_matrix());
|
||||
self.drawing_surface.canvas().concat(&modifiers);
|
||||
}
|
||||
|
||||
let transform = shape.transform.to_skia_matrix();
|
||||
|
||||
// Check transform-matrix code from common/src/app/common/geom/shapes/transforms.cljc
|
||||
let center = shape.bounds().center();
|
||||
let mut matrix = skia::Matrix::new_identity();
|
||||
matrix.pre_translate(center);
|
||||
matrix.pre_concat(&transform);
|
||||
|
||||
// Transform the shape in the center
|
||||
let mut matrix = shape.transform.clone();
|
||||
matrix.post_translate(center);
|
||||
matrix.pre_translate(-center);
|
||||
|
||||
self.drawing_surface.canvas().concat(&matrix);
|
||||
|
|
|
@ -67,11 +67,13 @@ fn draw_image_fill_in_container(
|
|||
canvas.clip_path(&oval_path, skia::ClipOp::Intersect, true);
|
||||
}
|
||||
Kind::Path(path) | Kind::Bool(_, path) => {
|
||||
canvas.clip_path(
|
||||
&path.to_skia_path().transform(&path_transform.unwrap()),
|
||||
skia::ClipOp::Intersect,
|
||||
true,
|
||||
);
|
||||
if let Some(path_transform) = path_transform {
|
||||
canvas.clip_path(
|
||||
&path.to_skia_path().transform(&path_transform),
|
||||
skia::ClipOp::Intersect,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
Kind::SVGRaw(_) => {
|
||||
canvas.clip_rect(container, skia::ClipOp::Intersect, true);
|
||||
|
@ -79,7 +81,9 @@ fn draw_image_fill_in_container(
|
|||
}
|
||||
|
||||
// Draw the image with the calculated destination rectangle
|
||||
canvas.draw_image_rect(image.unwrap(), None, dest_rect, &paint);
|
||||
if let Some(image) = image {
|
||||
canvas.draw_image_rect(image, None, dest_rect, &paint);
|
||||
}
|
||||
|
||||
// Restore the canvas to remove the clipping
|
||||
canvas.restore();
|
||||
|
@ -110,11 +114,14 @@ pub fn render(render_state: &mut RenderState, shape: &Shape, fill: &Fill) {
|
|||
(_, Kind::Path(path)) | (_, Kind::Bool(_, path)) => {
|
||||
let svg_attrs = &shape.svg_attrs;
|
||||
let mut skia_path = &mut path.to_skia_path();
|
||||
skia_path = skia_path.transform(&path_transform.unwrap());
|
||||
if let Some("evenodd") = svg_attrs.get("fill-rule").map(String::as_str) {
|
||||
skia_path.set_fill_type(skia::PathFillType::EvenOdd);
|
||||
|
||||
if let Some(path_transform) = path_transform {
|
||||
skia_path = skia_path.transform(&path_transform);
|
||||
if let Some("evenodd") = svg_attrs.get("fill-rule").map(String::as_str) {
|
||||
skia_path.set_fill_type(skia::PathFillType::EvenOdd);
|
||||
}
|
||||
canvas.draw_path(&skia_path, &fill.to_paint(&selrect));
|
||||
}
|
||||
canvas.draw_path(&skia_path, &fill.to_paint(&selrect));
|
||||
}
|
||||
(_, _) => todo!(),
|
||||
}
|
||||
|
|
|
@ -3,12 +3,13 @@ use skia_safe as skia;
|
|||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::matrix::Matrix;
|
||||
use crate::render::BlendMode;
|
||||
use skia::Matrix;
|
||||
|
||||
mod blurs;
|
||||
mod bools;
|
||||
mod fills;
|
||||
mod modifiers;
|
||||
mod paths;
|
||||
mod shadows;
|
||||
mod strokes;
|
||||
|
@ -18,6 +19,7 @@ mod transform;
|
|||
pub use blurs::*;
|
||||
pub use bools::*;
|
||||
pub use fills::*;
|
||||
pub use modifiers::*;
|
||||
pub use paths::*;
|
||||
pub use shadows::*;
|
||||
pub use strokes::*;
|
||||
|
@ -36,6 +38,50 @@ pub enum Kind {
|
|||
SVGRaw(SVGRaw),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ConstraintH {
|
||||
Left,
|
||||
Right,
|
||||
LeftRight,
|
||||
Center,
|
||||
Scale,
|
||||
}
|
||||
|
||||
impl ConstraintH {
|
||||
pub fn from(value: u8) -> Option<Self> {
|
||||
match value {
|
||||
0 => Some(Self::Left),
|
||||
1 => Some(Self::Right),
|
||||
2 => Some(Self::LeftRight),
|
||||
3 => Some(Self::Center),
|
||||
4 => Some(Self::Scale),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ConstraintV {
|
||||
Top,
|
||||
Bottom,
|
||||
TopBottom,
|
||||
Center,
|
||||
Scale,
|
||||
}
|
||||
|
||||
impl ConstraintV {
|
||||
pub fn from(value: u8) -> Option<Self> {
|
||||
match value {
|
||||
0 => Some(Self::Top),
|
||||
1 => Some(Self::Bottom),
|
||||
2 => Some(Self::TopBottom),
|
||||
3 => Some(Self::Center),
|
||||
4 => Some(Self::Scale),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Color = skia::Color;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -47,6 +93,8 @@ pub struct Shape {
|
|||
pub selrect: math::Rect,
|
||||
pub transform: Matrix,
|
||||
pub rotation: f32,
|
||||
pub constraint_h: Option<ConstraintH>,
|
||||
pub constraint_v: Option<ConstraintV>,
|
||||
pub clip_content: bool,
|
||||
pub fills: Vec<Fill>,
|
||||
pub strokes: Vec<Stroke>,
|
||||
|
@ -66,8 +114,10 @@ impl Shape {
|
|||
children: Vec::<Uuid>::new(),
|
||||
kind: Kind::Rect(math::Rect::new_empty(), None),
|
||||
selrect: math::Rect::new_empty(),
|
||||
transform: Matrix::identity(),
|
||||
transform: Matrix::default(),
|
||||
rotation: 0.,
|
||||
constraint_h: None,
|
||||
constraint_v: None,
|
||||
clip_content: true,
|
||||
fills: vec![],
|
||||
strokes: vec![],
|
||||
|
@ -111,13 +161,21 @@ impl Shape {
|
|||
}
|
||||
|
||||
pub fn set_transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) {
|
||||
self.transform = Matrix::new(a, b, c, d, e, f);
|
||||
self.transform = Matrix::new_all(a, c, e, b, d, f, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
pub fn set_opacity(&mut self, opacity: f32) {
|
||||
self.opacity = opacity;
|
||||
}
|
||||
|
||||
pub fn set_constraint_h(&mut self, constraint: Option<ConstraintH>) {
|
||||
self.constraint_h = constraint;
|
||||
}
|
||||
|
||||
pub fn set_constraint_v(&mut self, constraint: Option<ConstraintV>) {
|
||||
self.constraint_v = constraint;
|
||||
}
|
||||
|
||||
pub fn set_hidden(&mut self, value: bool) {
|
||||
self.hidden = value;
|
||||
}
|
||||
|
@ -330,7 +388,7 @@ impl Shape {
|
|||
let center = self.bounds().center();
|
||||
let mut matrix = skia::Matrix::new_identity();
|
||||
matrix.pre_translate(center);
|
||||
matrix.pre_concat(&self.transform.no_translation().to_skia_matrix().invert()?);
|
||||
matrix.pre_concat(&self.transform.invert()?);
|
||||
matrix.pre_translate(-center);
|
||||
|
||||
Some(matrix)
|
||||
|
|
|
@ -19,6 +19,13 @@ impl RawStopData {
|
|||
pub fn offset(&self) -> f32 {
|
||||
self.offset as f32 / 100.0
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: [u8; 5]) -> Self {
|
||||
Self {
|
||||
color: [bytes[0], bytes[1], bytes[2], bytes[3]],
|
||||
offset: bytes[4],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -37,7 +44,7 @@ impl Gradient {
|
|||
self.offsets.push(offset);
|
||||
}
|
||||
|
||||
fn to_linear_shader(&self, rect: &math::Rect) -> skia::Shader {
|
||||
fn to_linear_shader(&self, rect: &math::Rect) -> Option<skia::Shader> {
|
||||
let start = (
|
||||
rect.left + self.start.0 * rect.width(),
|
||||
rect.top + self.start.1 * rect.height(),
|
||||
|
@ -46,7 +53,7 @@ impl Gradient {
|
|||
rect.left + self.end.0 * rect.width(),
|
||||
rect.top + self.end.1 * rect.height(),
|
||||
);
|
||||
let shader = skia::shader::Shader::linear_gradient(
|
||||
skia::shader::Shader::linear_gradient(
|
||||
(start, end),
|
||||
self.colors.as_slice(),
|
||||
self.offsets.as_slice(),
|
||||
|
@ -54,11 +61,9 @@ impl Gradient {
|
|||
None,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
shader
|
||||
}
|
||||
|
||||
fn to_radial_shader(&self, rect: &math::Rect) -> skia::Shader {
|
||||
fn to_radial_shader(&self, rect: &math::Rect) -> Option<skia::Shader> {
|
||||
let center = skia::Point::new(
|
||||
rect.left + self.start.0 * rect.width(),
|
||||
rect.top + self.start.1 * rect.height(),
|
||||
|
@ -80,7 +85,7 @@ impl Gradient {
|
|||
transform.pre_scale((self.width * rect.width() / rect.height(), 1.), None);
|
||||
transform.pre_translate((-center.x, -center.y));
|
||||
|
||||
let shader = skia::shader::Shader::radial_gradient(
|
||||
skia::shader::Shader::radial_gradient(
|
||||
center,
|
||||
distance,
|
||||
self.colors.as_slice(),
|
||||
|
@ -89,8 +94,6 @@ impl Gradient {
|
|||
None,
|
||||
Some(&transform),
|
||||
)
|
||||
.unwrap();
|
||||
shader
|
||||
}
|
||||
}
|
||||
|
||||
|
|
40
render-wasm/src/shapes/modifiers.rs
Normal file
40
render-wasm/src/shapes/modifiers.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use skia_safe as skia;
|
||||
|
||||
use std::collections::HashSet;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::shapes::{Shape, TransformEntry};
|
||||
use crate::state::State;
|
||||
|
||||
fn propagate_shape(_state: &State, shape: &Shape, transform: skia::Matrix) -> Vec<TransformEntry> {
|
||||
let children: Vec<TransformEntry> = shape
|
||||
.children
|
||||
.iter()
|
||||
.map(|id| TransformEntry {
|
||||
id: id.clone(),
|
||||
transform,
|
||||
})
|
||||
.collect();
|
||||
|
||||
children
|
||||
}
|
||||
|
||||
pub fn propagate_modifiers(state: &State, modifiers: Vec<TransformEntry>) -> Vec<TransformEntry> {
|
||||
let mut entries = modifiers.clone();
|
||||
let mut processed = HashSet::<Uuid>::new();
|
||||
let mut result = Vec::<TransformEntry>::new();
|
||||
|
||||
// Propagate the transform to children
|
||||
while let Some(entry) = entries.pop() {
|
||||
if !processed.contains(&entry.id) {
|
||||
if let Some(shape) = state.shapes.get(&entry.id) {
|
||||
let mut children = propagate_shape(state, shape, entry.transform);
|
||||
entries.append(&mut children);
|
||||
processed.insert(entry.id);
|
||||
result.push(entry.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
use skia_safe as skia;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::matrix::Matrix;
|
||||
use crate::mem::SerializableResult;
|
||||
use crate::utils::{uuid_from_u32_quartet, uuid_to_u32_quartet};
|
||||
use skia::Matrix;
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
#[repr(C)]
|
||||
|
@ -12,7 +13,7 @@ pub struct TransformEntry {
|
|||
}
|
||||
|
||||
impl SerializableResult for TransformEntry {
|
||||
type BytesType = [u8; size_of::<TransformEntry>()];
|
||||
type BytesType = [u8; 40];
|
||||
|
||||
fn from_bytes(bytes: Self::BytesType) -> Self {
|
||||
let id = uuid_from_u32_quartet(
|
||||
|
@ -22,26 +23,35 @@ impl SerializableResult for TransformEntry {
|
|||
u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]),
|
||||
);
|
||||
|
||||
let transform = Matrix::new(
|
||||
let transform = Matrix::new_all(
|
||||
f32::from_le_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]),
|
||||
f32::from_le_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]),
|
||||
f32::from_le_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]),
|
||||
f32::from_le_bytes([bytes[28], bytes[29], bytes[30], bytes[31]]),
|
||||
f32::from_le_bytes([bytes[32], bytes[33], bytes[34], bytes[35]]),
|
||||
f32::from_le_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]),
|
||||
f32::from_le_bytes([bytes[28], bytes[29], bytes[30], bytes[31]]),
|
||||
f32::from_le_bytes([bytes[36], bytes[37], bytes[38], bytes[39]]),
|
||||
0.0,
|
||||
0.0,
|
||||
1.0,
|
||||
);
|
||||
|
||||
TransformEntry { id, transform }
|
||||
}
|
||||
|
||||
fn as_bytes(&self) -> Self::BytesType {
|
||||
let mut result: [u8; 40] = [0; 40];
|
||||
let mut result: Self::BytesType = [0; 40];
|
||||
let (a, b, c, d) = uuid_to_u32_quartet(&self.id);
|
||||
result[0..4].clone_from_slice(&a.to_le_bytes());
|
||||
result[4..8].clone_from_slice(&b.to_le_bytes());
|
||||
result[8..12].clone_from_slice(&c.to_le_bytes());
|
||||
result[12..16].clone_from_slice(&d.to_le_bytes());
|
||||
result[16..40].clone_from_slice(&self.transform.as_bytes());
|
||||
|
||||
result[16..20].clone_from_slice(&self.transform[0].to_le_bytes());
|
||||
result[20..24].clone_from_slice(&self.transform[3].to_le_bytes());
|
||||
result[24..28].clone_from_slice(&self.transform[1].to_le_bytes());
|
||||
result[28..32].clone_from_slice(&self.transform[4].to_le_bytes());
|
||||
result[32..36].clone_from_slice(&self.transform[2].to_le_bytes());
|
||||
result[36..40].clone_from_slice(&self.transform[5].to_le_bytes());
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -61,7 +71,7 @@ mod tests {
|
|||
fn test_serialization() {
|
||||
let entry = TransformEntry {
|
||||
id: uuid!("550e8400-e29b-41d4-a716-446655440000"),
|
||||
transform: Matrix::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
|
||||
transform: Matrix::new_all(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 0.0, 0.0, 1.0),
|
||||
};
|
||||
|
||||
let bytes = entry.as_bytes();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::matrix;
|
||||
use skia_safe as skia;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -17,7 +16,7 @@ pub(crate) struct State<'a> {
|
|||
pub current_id: Option<Uuid>,
|
||||
pub current_shape: Option<&'a mut Shape>,
|
||||
pub shapes: HashMap<Uuid, Shape>,
|
||||
pub modifiers: HashMap<Uuid, matrix::Matrix>,
|
||||
pub modifiers: HashMap<Uuid, skia::Matrix>,
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
|
|
Loading…
Add table
Reference in a new issue