mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 02:48:12 +08:00
Restore functionality of GPU infrastructure (#1797)
* Update gpu nodes to compile again Restructure `gpu-executor` and `wgpu-executor` And libssl to nix shell Fix graphene-cli and add half percision color format Fix texture scaling Remove vulkan executor Fix compile errors Improve execution request deduplication * Fix warnings * Fix graph compile issues * Code review * Remove test file * Fix lint * Wip make node futures send * Make futures Send on non wasm targets * Fix warnings * Fix nested use of block_on --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
@@ -16,6 +16,12 @@ use glam::DAffine2;
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct SurfaceId(pub u64);
|
||||
|
||||
impl core::fmt::Display for SurfaceId {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.write_fmt(format_args!("{}", self.0))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct SurfaceFrame {
|
||||
@@ -30,6 +36,17 @@ impl Hash for SurfaceFrame {
|
||||
}
|
||||
}
|
||||
|
||||
impl Transform for SurfaceFrame {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
self.transform
|
||||
}
|
||||
}
|
||||
impl TransformMut for SurfaceFrame {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
&mut self.transform
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl StaticType for SurfaceFrame {
|
||||
type Static = SurfaceFrame;
|
||||
}
|
||||
@@ -48,6 +65,10 @@ pub struct SurfaceHandle<Surface> {
|
||||
pub surface_id: SurfaceId,
|
||||
pub surface: Surface,
|
||||
}
|
||||
// #[cfg(target_arch = "wasm32")]
|
||||
// unsafe impl<T: dyn_any::WasmNotSend> Send for SurfaceHandle<T> {}
|
||||
// #[cfg(target_arch = "wasm32")]
|
||||
// unsafe impl<T: dyn_any::WasmNotSync> Sync for SurfaceHandle<T> {}
|
||||
|
||||
unsafe impl<T: 'static> StaticType for SurfaceHandle<T> {
|
||||
type Static = SurfaceHandle<T>;
|
||||
@@ -83,7 +104,10 @@ impl<'a, Surface> Drop for SurfaceHandle<'a, Surface> {
|
||||
}
|
||||
}*/
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub type ResourceFuture = Pin<Box<dyn Future<Output = Result<Arc<[u8]>, ApplicationError>>>>;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub type ResourceFuture = Pin<Box<dyn Future<Output = Result<Arc<[u8]>, ApplicationError>> + Send>>;
|
||||
|
||||
pub trait ApplicationIo {
|
||||
type Surface;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
use crate::application_io::SurfaceHandleFrame;
|
||||
use crate::raster::{BlendMode, ImageFrame};
|
||||
use crate::renderer::GraphicElementRendered;
|
||||
use crate::transform::Footprint;
|
||||
use crate::vector::VectorData;
|
||||
use crate::{Color, Node};
|
||||
use crate::{Color, Node, SurfaceFrame};
|
||||
|
||||
use bezier_rs::BezierHandles;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use node_macro::node_fn;
|
||||
|
||||
use core::future::Future;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use glam::{DAffine2, DVec2, IVec2, UVec2};
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
pub mod renderer;
|
||||
|
||||
@@ -67,6 +68,8 @@ pub enum GraphicElement {
|
||||
VectorData(Box<VectorData>),
|
||||
/// A bitmap image with a finite position and extent, equivalent to the SVG <image> tag: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/image
|
||||
ImageFrame(ImageFrame<Color>),
|
||||
/// A Canvas evement
|
||||
Surface(SurfaceFrame),
|
||||
}
|
||||
|
||||
// TODO: Can this be removed? It doesn't necessarily make that much sense to have a default when, instead, the entire GraphicElement just shouldn't exist if there's no specific content to assign it.
|
||||
@@ -127,10 +130,10 @@ pub struct ConstructLayerNode<Stack, GraphicElement> {
|
||||
}
|
||||
|
||||
#[node_fn(ConstructLayerNode)]
|
||||
async fn construct_layer<Data: Into<GraphicElement>, Fut1: Future<Output = GraphicGroup>, Fut2: Future<Output = Data>>(
|
||||
async fn construct_layer<Data: Into<GraphicElement> + Send>(
|
||||
footprint: crate::transform::Footprint,
|
||||
mut stack: impl Node<crate::transform::Footprint, Output = Fut1>,
|
||||
graphic_element: impl Node<crate::transform::Footprint, Output = Fut2>,
|
||||
mut stack: impl Node<crate::transform::Footprint, Output = GraphicGroup>,
|
||||
graphic_element: impl Node<crate::transform::Footprint, Output = Data>,
|
||||
) -> GraphicGroup {
|
||||
let graphic_element = self.graphic_element.eval(footprint).await;
|
||||
let mut stack = self.stack.eval(footprint).await;
|
||||
@@ -162,9 +165,9 @@ pub struct ConstructArtboardNode<Contents, Label, Location, Dimensions, Backgrou
|
||||
}
|
||||
|
||||
#[node_fn(ConstructArtboardNode)]
|
||||
async fn construct_artboard<Fut: Future<Output = GraphicGroup>>(
|
||||
async fn construct_artboard(
|
||||
mut footprint: Footprint,
|
||||
contents: impl Node<Footprint, Output = Fut>,
|
||||
contents: impl Node<Footprint, Output = GraphicGroup>,
|
||||
label: String,
|
||||
location: IVec2,
|
||||
dimensions: IVec2,
|
||||
@@ -189,11 +192,7 @@ pub struct AddArtboardNode<ArtboardGroup, Artboard> {
|
||||
}
|
||||
|
||||
#[node_fn(AddArtboardNode)]
|
||||
async fn add_artboard<Data: Into<Artboard>, Fut1: Future<Output = ArtboardGroup>, Fut2: Future<Output = Data>>(
|
||||
footprint: Footprint,
|
||||
artboards: impl Node<Footprint, Output = Fut1>,
|
||||
artboard: impl Node<Footprint, Output = Fut2>,
|
||||
) -> ArtboardGroup {
|
||||
async fn add_artboard<Data: Into<Artboard> + Send>(footprint: Footprint, artboards: impl Node<Footprint, Output = ArtboardGroup>, artboard: impl Node<Footprint, Output = Data>) -> ArtboardGroup {
|
||||
let artboard = self.artboard.eval(footprint).await;
|
||||
let mut artboards = self.artboards.eval(footprint).await;
|
||||
|
||||
@@ -229,6 +228,25 @@ impl From<GraphicGroup> for GraphicElement {
|
||||
GraphicElement::GraphicGroup(graphic_group)
|
||||
}
|
||||
}
|
||||
impl From<SurfaceFrame> for GraphicElement {
|
||||
fn from(surface: SurfaceFrame) -> Self {
|
||||
GraphicElement::Surface(surface)
|
||||
}
|
||||
}
|
||||
impl From<alloc::sync::Arc<SurfaceHandleFrame<HtmlCanvasElement>>> for GraphicElement {
|
||||
fn from(surface: alloc::sync::Arc<SurfaceHandleFrame<HtmlCanvasElement>>) -> Self {
|
||||
let surface_id = surface.surface_handle.surface_id;
|
||||
let transform = surface.transform;
|
||||
GraphicElement::Surface(SurfaceFrame { surface_id, transform })
|
||||
}
|
||||
}
|
||||
impl From<SurfaceHandleFrame<HtmlCanvasElement>> for GraphicElement {
|
||||
fn from(surface: SurfaceHandleFrame<HtmlCanvasElement>) -> Self {
|
||||
let surface_id = surface.surface_handle.surface_id;
|
||||
let transform = surface.transform;
|
||||
GraphicElement::Surface(SurfaceFrame { surface_id, transform })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for GraphicGroup {
|
||||
type Target = Vec<GraphicElement>;
|
||||
@@ -287,72 +305,3 @@ impl GraphicGroup {
|
||||
tree
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphicElement {
|
||||
fn to_usvg_node(&self) -> usvg::Node {
|
||||
fn to_transform(transform: DAffine2) -> usvg::Transform {
|
||||
let cols = transform.to_cols_array();
|
||||
usvg::Transform::from_row(cols[0] as f32, cols[1] as f32, cols[2] as f32, cols[3] as f32, cols[4] as f32, cols[5] as f32)
|
||||
}
|
||||
|
||||
match self {
|
||||
GraphicElement::VectorData(vector_data) => {
|
||||
use usvg::tiny_skia_path::PathBuilder;
|
||||
let mut builder = PathBuilder::new();
|
||||
|
||||
let transform = to_transform(vector_data.transform);
|
||||
for subpath in vector_data.stroke_bezier_paths() {
|
||||
let start = vector_data.transform.transform_point2(subpath[0].anchor);
|
||||
builder.move_to(start.x as f32, start.y as f32);
|
||||
for bezier in subpath.iter() {
|
||||
bezier.apply_transformation(|pos| vector_data.transform.transform_point2(pos));
|
||||
let end = bezier.end;
|
||||
match bezier.handles {
|
||||
BezierHandles::Linear => builder.line_to(end.x as f32, end.y as f32),
|
||||
BezierHandles::Quadratic { handle } => builder.quad_to(handle.x as f32, handle.y as f32, end.x as f32, end.y as f32),
|
||||
BezierHandles::Cubic { handle_start, handle_end } => {
|
||||
builder.cubic_to(handle_start.x as f32, handle_start.y as f32, handle_end.x as f32, handle_end.y as f32, end.x as f32, end.y as f32)
|
||||
}
|
||||
}
|
||||
}
|
||||
if subpath.closed {
|
||||
builder.close()
|
||||
}
|
||||
}
|
||||
let path = builder.finish().unwrap();
|
||||
let mut path = usvg::Path::new(path.into());
|
||||
path.abs_transform = transform;
|
||||
// TODO: use proper style
|
||||
path.fill = None;
|
||||
path.stroke = Some(usvg::Stroke::default());
|
||||
usvg::Node::Path(Box::new(path))
|
||||
}
|
||||
GraphicElement::ImageFrame(image_frame) => {
|
||||
if image_frame.image.width * image_frame.image.height == 0 {
|
||||
return usvg::Node::Group(Box::default());
|
||||
}
|
||||
let png = image_frame.image.to_png();
|
||||
usvg::Node::Image(Box::new(usvg::Image {
|
||||
id: String::new(),
|
||||
abs_transform: to_transform(image_frame.transform),
|
||||
visibility: usvg::Visibility::Visible,
|
||||
view_box: usvg::ViewBox {
|
||||
rect: usvg::NonZeroRect::from_xywh(0., 0., 1., 1.).unwrap(),
|
||||
aspect: usvg::AspectRatio::default(),
|
||||
},
|
||||
rendering_mode: usvg::ImageRendering::OptimizeSpeed,
|
||||
kind: usvg::ImageKind::PNG(png.into()),
|
||||
bounding_box: None,
|
||||
}))
|
||||
}
|
||||
GraphicElement::GraphicGroup(group) => {
|
||||
let mut group_element = usvg::Group::default();
|
||||
|
||||
for element in group.iter() {
|
||||
group_element.children.push(element.to_usvg_node());
|
||||
}
|
||||
usvg::Node::Group(Box::new(group_element))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
mod quad;
|
||||
|
||||
use crate::raster::bbox::Bbox;
|
||||
use crate::raster::{BlendMode, Image, ImageFrame};
|
||||
use crate::transform::Transform;
|
||||
use crate::uuid::generate_uuid;
|
||||
use crate::vector::PointId;
|
||||
use crate::SurfaceFrame;
|
||||
use crate::{vector::VectorData, Artboard, Color, GraphicElement, GraphicGroup};
|
||||
pub use quad::Quad;
|
||||
|
||||
@@ -489,6 +491,39 @@ impl GraphicElementRendered for crate::ArtboardGroup {
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphicElementRendered for SurfaceFrame {
|
||||
fn render_svg(&self, render: &mut SvgRender, _render_params: &RenderParams) {
|
||||
let transform = self.transform;
|
||||
let (width, height) = (transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length());
|
||||
let matrix = (transform * DAffine2::from_scale((width, height).into()).inverse())
|
||||
.to_cols_array()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(String::new(), |val, (i, entry)| val + &(entry.to_string() + if i == 5 { "" } else { "," }));
|
||||
|
||||
let canvas = format!(
|
||||
r#"<foreignObject width="{}" height="{}" transform="matrix({})"><div data-canvas-placeholder="canvas{}"></div></foreignObject>"#,
|
||||
width.abs(),
|
||||
height.abs(),
|
||||
matrix,
|
||||
self.surface_id
|
||||
);
|
||||
render.svg.push(canvas.into())
|
||||
}
|
||||
|
||||
fn bounding_box(&self, transform: DAffine2) -> Option<[DVec2; 2]> {
|
||||
let bbox = Bbox::from_transform(transform);
|
||||
let aabb = bbox.to_axis_aligned_bbox();
|
||||
Some([aabb.start, aabb.end])
|
||||
}
|
||||
|
||||
fn add_click_targets(&self, _click_targets: &mut Vec<ClickTarget>) {}
|
||||
|
||||
fn contains_artboard(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphicElementRendered for ImageFrame<Color> {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams) {
|
||||
let transform: String = format_transform_matrix(self.transform * render.transform);
|
||||
@@ -559,6 +594,7 @@ impl GraphicElementRendered for GraphicElement {
|
||||
GraphicElement::VectorData(vector_data) => vector_data.render_svg(render, render_params),
|
||||
GraphicElement::ImageFrame(image_frame) => image_frame.render_svg(render, render_params),
|
||||
GraphicElement::GraphicGroup(graphic_group) => graphic_group.render_svg(render, render_params),
|
||||
GraphicElement::Surface(surface) => surface.render_svg(render, render_params),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,6 +603,7 @@ impl GraphicElementRendered for GraphicElement {
|
||||
GraphicElement::VectorData(vector_data) => GraphicElementRendered::bounding_box(&**vector_data, transform),
|
||||
GraphicElement::ImageFrame(image_frame) => image_frame.bounding_box(transform),
|
||||
GraphicElement::GraphicGroup(graphic_group) => graphic_group.bounding_box(transform),
|
||||
GraphicElement::Surface(surface) => surface.bounding_box(transform),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,6 +612,7 @@ impl GraphicElementRendered for GraphicElement {
|
||||
GraphicElement::VectorData(vector_data) => vector_data.add_click_targets(click_targets),
|
||||
GraphicElement::ImageFrame(image_frame) => image_frame.add_click_targets(click_targets),
|
||||
GraphicElement::GraphicGroup(graphic_group) => graphic_group.add_click_targets(click_targets),
|
||||
GraphicElement::Surface(surface) => surface.add_click_targets(click_targets),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,6 +621,7 @@ impl GraphicElementRendered for GraphicElement {
|
||||
GraphicElement::VectorData(vector_data) => vector_data.to_usvg_node(),
|
||||
GraphicElement::ImageFrame(image_frame) => image_frame.to_usvg_node(),
|
||||
GraphicElement::GraphicGroup(graphic_group) => graphic_group.to_usvg_node(),
|
||||
GraphicElement::Surface(surface) => surface.to_usvg_node(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,6 +630,7 @@ impl GraphicElementRendered for GraphicElement {
|
||||
GraphicElement::VectorData(vector_data) => vector_data.contains_artboard(),
|
||||
GraphicElement::ImageFrame(image_frame) => image_frame.contains_artboard(),
|
||||
GraphicElement::GraphicGroup(graphic_group) => graphic_group.contains_artboard(),
|
||||
GraphicElement::Surface(surface) => surface.contains_artboard(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ extern crate alloc;
|
||||
#[cfg_attr(feature = "log", macro_use)]
|
||||
#[cfg(feature = "log")]
|
||||
extern crate log;
|
||||
pub use crate as graphene_core;
|
||||
|
||||
pub mod consts;
|
||||
pub mod generic;
|
||||
@@ -176,3 +177,5 @@ pub use crate::application_io::{SurfaceFrame, SurfaceId};
|
||||
pub type WasmSurfaceHandle = application_io::SurfaceHandle<web_sys::HtmlCanvasElement>;
|
||||
#[cfg(feature = "wasm")]
|
||||
pub type WasmSurfaceHandleFrame = application_io::SurfaceHandleFrame<web_sys::HtmlCanvasElement>;
|
||||
|
||||
pub use dyn_any::{WasmNotSend, WasmNotSync};
|
||||
|
||||
@@ -1,46 +1,49 @@
|
||||
use crate::Node;
|
||||
use crate::{Node, WasmNotSend};
|
||||
use core::future::Future;
|
||||
use core::ops::Deref;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::sync::Arc;
|
||||
use core::cell::Cell;
|
||||
use core::pin::Pin;
|
||||
use dyn_any::DynFuture;
|
||||
|
||||
/// Caches the output of a given Node and acts as a proxy
|
||||
#[derive(Default)]
|
||||
pub struct MemoNode<T, CachedNode> {
|
||||
cache: Cell<Option<T>>,
|
||||
cache: Arc<Mutex<Option<T>>>,
|
||||
node: CachedNode,
|
||||
}
|
||||
impl<'i, 'o: 'i, T: 'i + Clone + 'o, CachedNode: 'i> Node<'i, ()> for MemoNode<T, CachedNode>
|
||||
impl<'i, 'o: 'i, T: 'i + Clone + 'o + WasmNotSend, CachedNode: 'i> Node<'i, ()> for MemoNode<T, CachedNode>
|
||||
where
|
||||
CachedNode: for<'any_input> Node<'any_input, ()>,
|
||||
for<'a> <CachedNode as Node<'a, ()>>::Output: core::future::Future<Output = T> + 'a,
|
||||
for<'a> <CachedNode as Node<'a, ()>>::Output: core::future::Future<Output = T> + WasmNotSend,
|
||||
{
|
||||
// TODO: This should return a reference to the cached cached_value
|
||||
// but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty accurate xD
|
||||
type Output = Pin<Box<dyn Future<Output = T> + 'i>>;
|
||||
fn eval(&'i self, input: ()) -> Pin<Box<dyn Future<Output = T> + 'i>> {
|
||||
Box::pin(async move {
|
||||
if let Some(cached_value) = self.cache.take() {
|
||||
self.cache.set(Some(cached_value.clone()));
|
||||
cached_value
|
||||
} else {
|
||||
let value = self.node.eval(input).await;
|
||||
self.cache.set(Some(value.clone()));
|
||||
type Output = DynFuture<'i, T>;
|
||||
fn eval(&'i self, input: ()) -> Self::Output {
|
||||
if let Some(cached_value) = self.cache.lock().as_ref().unwrap().deref() {
|
||||
let data = cached_value.clone();
|
||||
Box::pin(async move { data })
|
||||
} else {
|
||||
let fut = self.node.eval(input);
|
||||
let cache = self.cache.clone();
|
||||
Box::pin(async move {
|
||||
let value = fut.await;
|
||||
*cache.lock().unwrap() = Some(value.clone());
|
||||
value
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&self) {
|
||||
self.cache.set(None);
|
||||
self.cache.lock().unwrap().take();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, CachedNode> MemoNode<T, CachedNode> {
|
||||
pub const fn new(node: CachedNode) -> MemoNode<T, CachedNode> {
|
||||
MemoNode { cache: Cell::new(None), node }
|
||||
pub fn new(node: CachedNode) -> MemoNode<T, CachedNode> {
|
||||
MemoNode { cache: Default::default(), node }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,41 +53,43 @@ impl<T, CachedNode> MemoNode<T, CachedNode> {
|
||||
/// use with caution.
|
||||
#[derive(Default)]
|
||||
pub struct ImpureMemoNode<I, T, CachedNode> {
|
||||
cache: Cell<Option<T>>,
|
||||
cache: Arc<Mutex<Option<T>>>,
|
||||
node: CachedNode,
|
||||
_phantom: std::marker::PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<'i, 'o: 'i, I: 'i, T: 'i + Clone + 'o, CachedNode: 'i> Node<'i, I> for ImpureMemoNode<I, T, CachedNode>
|
||||
impl<'i, 'o: 'i, I: 'i, T: 'i + Clone + 'o + WasmNotSend, CachedNode: 'i> Node<'i, I> for ImpureMemoNode<I, T, CachedNode>
|
||||
where
|
||||
CachedNode: for<'any_input> Node<'any_input, I>,
|
||||
for<'a> <CachedNode as Node<'a, I>>::Output: core::future::Future<Output = T> + 'a,
|
||||
for<'a> <CachedNode as Node<'a, I>>::Output: core::future::Future<Output = T> + WasmNotSend,
|
||||
{
|
||||
// TODO: This should return a reference to the cached cached_value
|
||||
// but that requires a lot of lifetime magic <- This was suggested by copilot but is pretty accurate xD
|
||||
type Output = Pin<Box<dyn Future<Output = T> + 'i>>;
|
||||
fn eval(&'i self, input: I) -> Pin<Box<dyn Future<Output = T> + 'i>> {
|
||||
Box::pin(async move {
|
||||
if let Some(cached_value) = self.cache.take() {
|
||||
self.cache.set(Some(cached_value.clone()));
|
||||
cached_value
|
||||
} else {
|
||||
let value = self.node.eval(input).await;
|
||||
self.cache.set(Some(value.clone()));
|
||||
type Output = DynFuture<'i, T>;
|
||||
fn eval(&'i self, input: I) -> Self::Output {
|
||||
if let Some(cached_value) = self.cache.lock().as_ref().unwrap().deref() {
|
||||
let data = cached_value.clone();
|
||||
Box::pin(async move { data })
|
||||
} else {
|
||||
let fut = self.node.eval(input);
|
||||
let cache = self.cache.clone();
|
||||
Box::pin(async move {
|
||||
let value = fut.await;
|
||||
*cache.lock().unwrap() = Some(value.clone());
|
||||
value
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&self) {
|
||||
self.cache.set(None);
|
||||
self.cache.lock().unwrap().take();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I, CachedNode> ImpureMemoNode<I, T, CachedNode> {
|
||||
pub const fn new(node: CachedNode) -> ImpureMemoNode<I, T, CachedNode> {
|
||||
pub fn new(node: CachedNode) -> ImpureMemoNode<I, T, CachedNode> {
|
||||
ImpureMemoNode {
|
||||
cache: Cell::new(None),
|
||||
cache: Default::default(),
|
||||
node,
|
||||
_phantom: core::marker::PhantomData,
|
||||
}
|
||||
@@ -102,37 +107,38 @@ pub struct IORecord<I, O> {
|
||||
/// Caches the output of the last graph evaluation for introspection
|
||||
#[derive(Default)]
|
||||
pub struct MonitorNode<I, T, N> {
|
||||
io: Cell<Option<Arc<IORecord<I, T>>>>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
io: Arc<Mutex<Option<Arc<IORecord<I, T>>>>>,
|
||||
node: N,
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'i, 'a: 'i, T, I, N> Node<'i, I> for MonitorNode<I, T, N>
|
||||
impl<'i, T, I, N> Node<'i, I> for MonitorNode<I, T, N>
|
||||
where
|
||||
I: Clone + 'static,
|
||||
<N as Node<'i, I>>::Output: Future<Output = T>,
|
||||
T: Clone + 'static,
|
||||
N: Node<'i, I>,
|
||||
I: Clone + 'static + Send + Sync,
|
||||
T: Clone + 'static + Send + Sync,
|
||||
for<'a> N: Node<'a, I, Output: Future<Output = T> + WasmNotSend> + 'i,
|
||||
{
|
||||
type Output = Pin<Box<dyn Future<Output = T> + 'i>>;
|
||||
type Output = DynFuture<'i, T>;
|
||||
fn eval(&'i self, input: I) -> Self::Output {
|
||||
let io = self.io.clone();
|
||||
let output_fut = self.node.eval(input.clone());
|
||||
Box::pin(async move {
|
||||
let output = self.node.eval(input.clone()).await;
|
||||
self.io.set(Some(Arc::new(IORecord { input, output: output.clone() })));
|
||||
let output = output_fut.await;
|
||||
*io.lock().unwrap() = Some(Arc::new(IORecord { input, output: output.clone() }));
|
||||
output
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize(&self) -> Option<Arc<dyn core::any::Any>> {
|
||||
let io = self.io.take();
|
||||
self.io.set(io.clone());
|
||||
let io = self.io.lock().unwrap();
|
||||
(io).as_ref().map(|output| output.clone() as Arc<dyn core::any::Any>)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<I, T, N> MonitorNode<I, T, N> {
|
||||
pub const fn new(node: N) -> MonitorNode<I, T, N> {
|
||||
MonitorNode { io: Cell::new(None), node }
|
||||
pub fn new(node: N) -> MonitorNode<I, T, N> {
|
||||
MonitorNode { io: Arc::new(Mutex::new(None)), node }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ pub struct IntoNode<I, O> {
|
||||
#[node_macro::node_fn(IntoNode<_I, _O>)]
|
||||
async fn into<_I, _O>(input: _I) -> _O
|
||||
where
|
||||
_I: Into<_O>,
|
||||
_I: Into<_O> + Sync + Send,
|
||||
{
|
||||
input.into()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use core::hash::Hash;
|
||||
use half::f16;
|
||||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -14,6 +15,78 @@ use super::{
|
||||
discrete_srgb::{float_to_srgb_u8, srgb_u8_to_float},
|
||||
Alpha, AssociatedAlpha, Luminance, LuminanceMut, Pixel, RGBMut, Rec709Primaries, RGB, SRGB,
|
||||
};
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, Pod, Zeroable)]
|
||||
pub struct RGBA16F {
|
||||
red: f16,
|
||||
green: f16,
|
||||
blue: f16,
|
||||
alpha: f16,
|
||||
}
|
||||
|
||||
impl From<Color> for RGBA16F {
|
||||
#[inline(always)]
|
||||
fn from(c: Color) -> Self {
|
||||
Self {
|
||||
red: f16::from_f32(c.r()),
|
||||
green: f16::from_f32(c.g()),
|
||||
blue: f16::from_f32(c.b()),
|
||||
alpha: f16::from_f32(c.a()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Luminance for RGBA16F {
|
||||
type LuminanceChannel = f32;
|
||||
#[inline(always)]
|
||||
fn luminance(&self) -> f32 {
|
||||
// TODO: verify this is correct for sRGB
|
||||
0.2126 * self.red() + 0.7152 * self.green() + 0.0722 * self.blue()
|
||||
}
|
||||
}
|
||||
|
||||
impl RGB for RGBA16F {
|
||||
type ColorChannel = f32;
|
||||
#[inline(always)]
|
||||
fn red(&self) -> f32 {
|
||||
self.red.to_f32()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn green(&self) -> f32 {
|
||||
self.green.to_f32()
|
||||
}
|
||||
#[inline(always)]
|
||||
fn blue(&self) -> f32 {
|
||||
self.blue.to_f32()
|
||||
}
|
||||
}
|
||||
|
||||
impl Rec709Primaries for RGBA16F {}
|
||||
|
||||
impl Alpha for RGBA16F {
|
||||
type AlphaChannel = f32;
|
||||
#[inline(always)]
|
||||
fn alpha(&self) -> f32 {
|
||||
self.alpha.to_f32() / 255.
|
||||
}
|
||||
|
||||
const TRANSPARENT: Self = RGBA16F {
|
||||
red: f16::from_f32_const(0.),
|
||||
green: f16::from_f32_const(0.),
|
||||
blue: f16::from_f32_const(0.),
|
||||
alpha: f16::from_f32_const(0.),
|
||||
};
|
||||
|
||||
fn multiplied_alpha(&self, alpha: Self::AlphaChannel) -> Self {
|
||||
let alpha = alpha * 255.;
|
||||
let mut result = *self;
|
||||
result.alpha = f16::from_f32(alpha * self.alpha());
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl Pixel for RGBA16F {}
|
||||
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
@@ -33,7 +106,7 @@ impl From<Color> for SRGBA8 {
|
||||
red: float_to_srgb_u8(c.r()),
|
||||
green: float_to_srgb_u8(c.g()),
|
||||
blue: float_to_srgb_u8(c.b()),
|
||||
alpha: (c.a() * 255.0) as u8,
|
||||
alpha: (c.a() * 255.) as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,7 +118,7 @@ impl From<SRGBA8> for Color {
|
||||
red: srgb_u8_to_float(color.red),
|
||||
green: srgb_u8_to_float(color.green),
|
||||
blue: srgb_u8_to_float(color.blue),
|
||||
alpha: color.alpha as f32 / 255.0,
|
||||
alpha: color.alpha as f32 / 255.,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,15 +136,15 @@ impl RGB for SRGBA8 {
|
||||
type ColorChannel = f32;
|
||||
#[inline(always)]
|
||||
fn red(&self) -> f32 {
|
||||
self.red as f32 / 255.0
|
||||
self.red as f32 / 255.
|
||||
}
|
||||
#[inline(always)]
|
||||
fn green(&self) -> f32 {
|
||||
self.green as f32 / 255.0
|
||||
self.green as f32 / 255.
|
||||
}
|
||||
#[inline(always)]
|
||||
fn blue(&self) -> f32 {
|
||||
self.blue as f32 / 255.0
|
||||
self.blue as f32 / 255.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,13 +155,13 @@ impl Alpha for SRGBA8 {
|
||||
type AlphaChannel = f32;
|
||||
#[inline(always)]
|
||||
fn alpha(&self) -> f32 {
|
||||
self.alpha as f32 / 255.0
|
||||
self.alpha as f32 / 255.
|
||||
}
|
||||
|
||||
const TRANSPARENT: Self = SRGBA8 { red: 0, green: 0, blue: 0, alpha: 0 };
|
||||
|
||||
fn multiplied_alpha(&self, alpha: Self::AlphaChannel) -> Self {
|
||||
let alpha = alpha * 255.0;
|
||||
let alpha = alpha * 255.;
|
||||
let mut result = *self;
|
||||
result.alpha = (alpha * self.alpha()) as u8;
|
||||
result
|
||||
@@ -338,7 +411,7 @@ impl Color {
|
||||
#[inline(always)]
|
||||
pub fn from_rgba8_srgb(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
|
||||
let alpha = alpha as f32 / 255.;
|
||||
let map_range = |int_color| int_color as f32 / 255.0;
|
||||
let map_range = |int_color| int_color as f32 / 255.;
|
||||
Color {
|
||||
red: map_range(red),
|
||||
green: map_range(green),
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use core::future::Future;
|
||||
|
||||
use dyn_any::StaticType;
|
||||
use glam::DAffine2;
|
||||
|
||||
@@ -27,6 +25,12 @@ pub trait Transform {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Transform> Transform for &T {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
(*self).transform()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TransformMut: Transform {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2;
|
||||
fn translate(&mut self, offset: DVec2) {
|
||||
@@ -42,14 +46,6 @@ impl<P: Pixel> Transform for ImageFrame<P> {
|
||||
self.local_pivot(pivot)
|
||||
}
|
||||
}
|
||||
impl<P: Pixel> Transform for &ImageFrame<P> {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
self.transform
|
||||
}
|
||||
fn local_pivot(&self, pivot: DVec2) -> DVec2 {
|
||||
(*self).local_pivot(pivot)
|
||||
}
|
||||
}
|
||||
impl<P: Pixel> TransformMut for ImageFrame<P> {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
&mut self.transform
|
||||
@@ -60,11 +56,6 @@ impl Transform for GraphicGroup {
|
||||
self.transform
|
||||
}
|
||||
}
|
||||
impl Transform for &GraphicGroup {
|
||||
fn transform(&self) -> DAffine2 {
|
||||
self.transform
|
||||
}
|
||||
}
|
||||
impl TransformMut for GraphicGroup {
|
||||
fn transform_mut(&mut self) -> &mut DAffine2 {
|
||||
&mut self.transform
|
||||
@@ -76,6 +67,7 @@ impl Transform for GraphicElement {
|
||||
GraphicElement::VectorData(vector_shape) => vector_shape.transform(),
|
||||
GraphicElement::ImageFrame(image_frame) => image_frame.transform(),
|
||||
GraphicElement::GraphicGroup(graphic_group) => graphic_group.transform(),
|
||||
GraphicElement::Surface(surface) => surface.transform(),
|
||||
}
|
||||
}
|
||||
fn local_pivot(&self, pivot: DVec2) -> DVec2 {
|
||||
@@ -83,13 +75,7 @@ impl Transform for GraphicElement {
|
||||
GraphicElement::VectorData(vector_shape) => vector_shape.local_pivot(pivot),
|
||||
GraphicElement::ImageFrame(image_frame) => image_frame.local_pivot(pivot),
|
||||
GraphicElement::GraphicGroup(graphic_group) => graphic_group.local_pivot(pivot),
|
||||
}
|
||||
}
|
||||
fn decompose_scale(&self) -> DVec2 {
|
||||
match self {
|
||||
GraphicElement::VectorData(vector_shape) => vector_shape.decompose_scale(),
|
||||
GraphicElement::ImageFrame(image_frame) => image_frame.decompose_scale(),
|
||||
GraphicElement::GraphicGroup(graphic_group) => graphic_group.decompose_scale(),
|
||||
GraphicElement::Surface(surface) => surface.local_pivot(pivot),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,6 +85,7 @@ impl TransformMut for GraphicElement {
|
||||
GraphicElement::VectorData(vector_shape) => vector_shape.transform_mut(),
|
||||
GraphicElement::ImageFrame(image_frame) => image_frame.transform_mut(),
|
||||
GraphicElement::GraphicGroup(graphic_group) => graphic_group.transform_mut(),
|
||||
GraphicElement::Surface(surface) => surface.transform_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,16 +124,6 @@ impl TransformMut for DAffine2 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TransformNode<TransformTarget, Translation, Rotation, Scale, Shear, Pivot> {
|
||||
pub(crate) transform_target: TransformTarget,
|
||||
pub(crate) translate: Translation,
|
||||
pub(crate) rotate: Rotation,
|
||||
pub(crate) scale: Scale,
|
||||
pub(crate) shear: Shear,
|
||||
pub(crate) _pivot: Pivot,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, dyn_any::DynAny, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum RenderQuality {
|
||||
@@ -233,19 +210,26 @@ impl TransformMut for Footprint {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TransformNode<TransformTarget, Translation, Rotation, Scale, Shear, Pivot> {
|
||||
pub(crate) transform_target: TransformTarget,
|
||||
pub(crate) translate: Translation,
|
||||
pub(crate) rotate: Rotation,
|
||||
pub(crate) scale: Scale,
|
||||
pub(crate) shear: Shear,
|
||||
pub(crate) _pivot: Pivot,
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(TransformNode)]
|
||||
pub(crate) async fn transform_vector_data<Fut: Future>(
|
||||
pub(crate) async fn transform_vector_data<T: TransformMut>(
|
||||
mut footprint: Footprint,
|
||||
transform_target: impl Node<Footprint, Output = Fut>,
|
||||
transform_target: impl Node<Footprint, Output = T>,
|
||||
translate: DVec2,
|
||||
rotate: f64,
|
||||
scale: DVec2,
|
||||
shear: DVec2,
|
||||
_pivot: DVec2,
|
||||
) -> Fut::Output
|
||||
where
|
||||
Fut::Output: TransformMut,
|
||||
{
|
||||
) -> T {
|
||||
let modification = DAffine2::from_scale_angle_translation(scale, rotate, translate) * DAffine2::from_cols_array(&[1., shear.y, shear.x, 1., 0., 0.]);
|
||||
if !footprint.ignore_modifications {
|
||||
*footprint.transform_mut() = footprint.transform() * modification;
|
||||
|
||||
@@ -337,7 +337,7 @@ impl HandleId {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn assert_subpath_eq(generated: &Vec<bezier_rs::Subpath<PointId>>, expected: &[bezier_rs::Subpath<PointId>]) {
|
||||
fn assert_subpath_eq(generated: &[bezier_rs::Subpath<PointId>], expected: &[bezier_rs::Subpath<PointId>]) {
|
||||
assert_eq!(generated.len(), expected.len());
|
||||
for (generated, expected) in generated.iter().zip(expected) {
|
||||
assert_eq!(generated.manipulator_groups().len(), expected.manipulator_groups().len());
|
||||
|
||||
@@ -443,7 +443,7 @@ fn modify_existing() {
|
||||
false,
|
||||
),
|
||||
];
|
||||
let mut vector_data = VectorData::from_subpaths(&subpaths, false);
|
||||
let mut vector_data = VectorData::from_subpaths(subpaths, false);
|
||||
|
||||
let mut modify_new = VectorModification::create_from_vector(&vector_data);
|
||||
let mut modify_original = VectorModification::default();
|
||||
|
||||
@@ -4,7 +4,6 @@ use super::{PointId, SegmentId, StrokeId, VectorData};
|
||||
use crate::renderer::GraphicElementRendered;
|
||||
use crate::transform::{Footprint, Transform, TransformMut};
|
||||
use crate::{Color, GraphicGroup, Node};
|
||||
use core::future::Future;
|
||||
|
||||
use bezier_rs::{Cap, Join, Subpath, SubpathTValue, TValue};
|
||||
use glam::{DAffine2, DVec2};
|
||||
@@ -212,10 +211,10 @@ pub struct CopyToPoints<Points, Instance, RandomScaleMin, RandomScaleMax, Random
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(CopyToPoints)]
|
||||
async fn copy_to_points<I: GraphicElementRendered + Default + ConcatElement + TransformMut, FP: Future<Output = VectorData>, FI: Future<Output = I>>(
|
||||
async fn copy_to_points<I: GraphicElementRendered + Default + ConcatElement + TransformMut + Send>(
|
||||
footprint: Footprint,
|
||||
points: impl Node<Footprint, Output = FP>,
|
||||
instance: impl Node<Footprint, Output = FI>,
|
||||
points: impl Node<Footprint, Output = VectorData>,
|
||||
instance: impl Node<Footprint, Output = I>,
|
||||
random_scale_min: f64,
|
||||
random_scale_max: f64,
|
||||
random_scale_bias: f64,
|
||||
@@ -280,14 +279,14 @@ pub struct SamplePoints<VectorData, Spacing, StartOffset, StopOffset, AdaptiveSp
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(SamplePoints)]
|
||||
async fn sample_points<FV: Future<Output = VectorData>, FL: Future<Output = Vec<f64>>>(
|
||||
async fn sample_points(
|
||||
footprint: Footprint,
|
||||
mut vector_data: impl Node<Footprint, Output = FV>,
|
||||
mut vector_data: impl Node<Footprint, Output = VectorData>,
|
||||
spacing: f64,
|
||||
start_offset: f64,
|
||||
stop_offset: f64,
|
||||
adaptive_spacing: bool,
|
||||
lengths_of_segments_of_subpaths: impl Node<Footprint, Output = FL>,
|
||||
lengths_of_segments_of_subpaths: impl Node<Footprint, Output = Vec<f64>>,
|
||||
) -> VectorData {
|
||||
let vector_data = self.vector_data.eval(footprint).await;
|
||||
let lengths_of_segments_of_subpaths = self.lengths_of_segments_of_subpaths.eval(footprint).await;
|
||||
@@ -422,13 +421,7 @@ pub struct MorphNode<Source, Target, StartIndex, Time> {
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(MorphNode)]
|
||||
async fn morph<SourceFuture: Future<Output = VectorData>, TargetFuture: Future<Output = VectorData>>(
|
||||
footprint: Footprint,
|
||||
source: impl Node<Footprint, Output = SourceFuture>,
|
||||
target: impl Node<Footprint, Output = TargetFuture>,
|
||||
start_index: u32,
|
||||
time: f64,
|
||||
) -> VectorData {
|
||||
async fn morph(footprint: Footprint, source: impl Node<Footprint, Output = VectorData>, target: impl Node<Footprint, Output = VectorData>, start_index: u32, time: f64) -> VectorData {
|
||||
let source = self.source.eval(footprint).await;
|
||||
let target = self.target.eval(footprint).await;
|
||||
let mut result = VectorData::empty();
|
||||
@@ -516,7 +509,7 @@ pub struct AreaNode<VectorData> {
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(AreaNode)]
|
||||
async fn area_node<Fut: Future<Output = VectorData>>(empty: (), vector_data: impl Node<Footprint, Output = Fut>) -> f64 {
|
||||
async fn area_node(empty: (), vector_data: impl Node<Footprint, Output = VectorData>) -> f64 {
|
||||
let vector_data = self.vector_data.eval(Footprint::default()).await;
|
||||
|
||||
let mut area = 0.;
|
||||
@@ -534,7 +527,7 @@ pub struct CentroidNode<VectorData, CentroidType> {
|
||||
}
|
||||
|
||||
#[node_macro::node_fn(CentroidNode)]
|
||||
async fn centroid_node<Fut: Future<Output = VectorData>>(empty: (), vector_data: impl Node<Footprint, Output = Fut>, centroid_type: CentroidType) -> DVec2 {
|
||||
async fn centroid_node(empty: (), vector_data: impl Node<Footprint, Output = VectorData>, centroid_type: CentroidType) -> DVec2 {
|
||||
let vector_data = self.vector_data.eval(Footprint::default()).await;
|
||||
|
||||
if centroid_type == CentroidType::Area {
|
||||
@@ -594,11 +587,12 @@ mod test {
|
||||
|
||||
impl<'i, T: 'i, N: Node<'i, T> + Clone> Node<'i, T> for FutureWrapperNode<N>
|
||||
where
|
||||
N: Node<'i, T>,
|
||||
N: Node<'i, T, Output: Send>,
|
||||
{
|
||||
type Output = Pin<Box<dyn core::future::Future<Output = N::Output> + 'i>>;
|
||||
type Output = Pin<Box<dyn core::future::Future<Output = N::Output> + 'i + Send>>;
|
||||
fn eval(&'i self, input: T) -> Self::Output {
|
||||
Box::pin(async move { self.0.eval(input) })
|
||||
let result = self.0.eval(input);
|
||||
Box::pin(async move { result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user