mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-20 03:18:06 +08:00
Merge branch 'master' into fix-range
This commit is contained in:
@@ -403,7 +403,7 @@ mod test {
|
||||
blend_mode: BlendMode::Normal,
|
||||
},
|
||||
}],
|
||||
BrushCache::new_proto(),
|
||||
BrushCache::default(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(image.instance_ref_iter().next().unwrap().instance.width, 20);
|
||||
|
||||
@@ -6,11 +6,16 @@ use graphene_core::raster_types::CPU;
|
||||
use graphene_core::raster_types::Raster;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::hash::Hasher;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
|
||||
// TODO: This is a temporary hack, be sure to not reuse this when the brush is being rewritten.
|
||||
static NEXT_BRUSH_CACHE_IMPL_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[derive(Clone, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
struct BrushCacheImpl {
|
||||
unique_id: u64,
|
||||
// The full previous input that was cached.
|
||||
prev_input: Vec<BrushStroke>,
|
||||
|
||||
@@ -90,9 +95,29 @@ impl BrushCacheImpl {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BrushCacheImpl {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
unique_id: NEXT_BRUSH_CACHE_IMPL_ID.fetch_add(1, Ordering::SeqCst),
|
||||
prev_input: Vec::new(),
|
||||
background: Default::default(),
|
||||
blended_image: Default::default(),
|
||||
last_stroke_texture: Default::default(),
|
||||
brush_texture_cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BrushCacheImpl {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.unique_id == other.unique_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for BrushCacheImpl {
|
||||
// Zero hash.
|
||||
fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {}
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.unique_id.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
@@ -103,46 +128,26 @@ pub struct BrushPlan {
|
||||
pub first_stroke_point_skip: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct BrushCache {
|
||||
inner: Arc<Mutex<BrushCacheImpl>>,
|
||||
proto: bool,
|
||||
}
|
||||
|
||||
impl Default for BrushCache {
|
||||
fn default() -> Self {
|
||||
Self::new_proto()
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct BrushCache(Arc<Mutex<BrushCacheImpl>>);
|
||||
|
||||
// A bit of a cursed implementation to work around the current node system.
|
||||
// The original object is a 'prototype' that when cloned gives you a independent
|
||||
// new object. Any further clones however are all the same underlying cache object.
|
||||
impl Clone for BrushCache {
|
||||
fn clone(&self) -> Self {
|
||||
if self.proto {
|
||||
let inner_val = self.inner.lock().unwrap();
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(inner_val.clone())),
|
||||
proto: false,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
inner: Arc::clone(&self.inner),
|
||||
proto: false,
|
||||
}
|
||||
}
|
||||
Self(Arc::new(Mutex::new(self.0.lock().unwrap().clone())))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BrushCache {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if Arc::ptr_eq(&self.inner, &other.inner) {
|
||||
if Arc::ptr_eq(&self.0, &other.0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let s = self.inner.lock().unwrap();
|
||||
let o = other.inner.lock().unwrap();
|
||||
let s = self.0.lock().unwrap();
|
||||
let o = other.0.lock().unwrap();
|
||||
|
||||
*s == *o
|
||||
}
|
||||
@@ -150,35 +155,28 @@ impl PartialEq for BrushCache {
|
||||
|
||||
impl Hash for BrushCache {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.inner.lock().unwrap().hash(state);
|
||||
self.0.lock().unwrap().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl BrushCache {
|
||||
pub fn new_proto() -> Self {
|
||||
Self {
|
||||
inner: Default::default(),
|
||||
proto: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_brush_plan(&self, background: Instance<Raster<CPU>>, input: &[BrushStroke]) -> BrushPlan {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner.compute_brush_plan(background, input)
|
||||
}
|
||||
|
||||
pub fn cache_results(&self, input: Vec<BrushStroke>, blended_image: Instance<Raster<CPU>>, last_stroke_texture: Instance<Raster<CPU>>) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner.cache_results(input, blended_image, last_stroke_texture)
|
||||
}
|
||||
|
||||
pub fn get_cached_brush(&self, style: &BrushStyle) -> Option<Raster<CPU>> {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
let inner = self.0.lock().unwrap();
|
||||
inner.brush_texture_cache.get(style).cloned()
|
||||
}
|
||||
|
||||
pub fn store_brush(&self, style: BrushStyle, brush: Raster<CPU>) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner.brush_texture_cache.insert(style, brush);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artboard: Artboard, node_path: Vec<NodeId>) -> ArtboardGroupTable {
|
||||
pub async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artboard: Artboard, node_path: Vec<NodeId>) -> ArtboardGroupTable {
|
||||
// Get the penultimate element of the node path, or None if the path is too short.
|
||||
// This is used to get the ID of the user-facing "Artboard" node (which encapsulates this internal "Append Artboard" node).
|
||||
let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
|
||||
@@ -22,6 +22,7 @@ pub mod ops;
|
||||
pub mod raster;
|
||||
pub mod raster_types;
|
||||
pub mod registry;
|
||||
pub mod render_complexity;
|
||||
pub mod structural;
|
||||
pub mod text;
|
||||
pub mod transform;
|
||||
|
||||
61
node-graph/gcore/src/render_complexity.rs
Normal file
61
node-graph/gcore/src/render_complexity.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use crate::instances::Instances;
|
||||
use crate::raster_types::{CPU, GPU, Raster};
|
||||
use crate::vector::VectorData;
|
||||
use crate::{Artboard, Color, GraphicElement};
|
||||
use glam::DVec2;
|
||||
|
||||
pub trait RenderComplexity {
|
||||
fn render_complexity(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RenderComplexity> RenderComplexity for Instances<T> {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.instance_ref_iter().map(|instance| instance.instance.render_complexity()).fold(0, usize::saturating_add)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for Artboard {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.graphic_group.render_complexity()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for GraphicElement {
|
||||
fn render_complexity(&self) -> usize {
|
||||
match self {
|
||||
Self::GraphicGroup(instances) => instances.render_complexity(),
|
||||
Self::VectorData(instances) => instances.render_complexity(),
|
||||
Self::RasterDataCPU(instances) => instances.render_complexity(),
|
||||
Self::RasterDataGPU(instances) => instances.render_complexity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for VectorData {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.segment_domain.ids().len()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for Raster<CPU> {
|
||||
fn render_complexity(&self) -> usize {
|
||||
(self.width * self.height / 500) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for Raster<GPU> {
|
||||
fn render_complexity(&self) -> usize {
|
||||
// GPU textures currently can't have a thumbnail
|
||||
usize::MAX
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for String {}
|
||||
impl RenderComplexity for bool {}
|
||||
impl RenderComplexity for f32 {}
|
||||
impl RenderComplexity for f64 {}
|
||||
impl RenderComplexity for DVec2 {}
|
||||
impl RenderComplexity for Option<Color> {}
|
||||
impl RenderComplexity for Vec<Color> {}
|
||||
@@ -10,6 +10,7 @@ use graphene_core::instances::Instance;
|
||||
use graphene_core::math::quad::Quad;
|
||||
use graphene_core::raster::Image;
|
||||
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
||||
use graphene_core::render_complexity::RenderComplexity;
|
||||
use graphene_core::transform::{Footprint, Transform};
|
||||
use graphene_core::uuid::{NodeId, generate_uuid};
|
||||
use graphene_core::vector::VectorDataTable;
|
||||
@@ -204,7 +205,7 @@ pub struct RenderMetadata {
|
||||
}
|
||||
|
||||
// TODO: Rename to "Graphical"
|
||||
pub trait GraphicElementRendered: BoundingBox {
|
||||
pub trait GraphicElementRendered: BoundingBox + RenderComplexity {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams);
|
||||
|
||||
#[cfg(feature = "vello")]
|
||||
@@ -1151,7 +1152,7 @@ impl GraphicElementRendered for GraphicElement {
|
||||
}
|
||||
|
||||
/// Used to stop rust complaining about upstream traits adding display implementations to `Option<Color>`. This would not be an issue as we control that crate.
|
||||
trait Primitive: std::fmt::Display + BoundingBox {}
|
||||
trait Primitive: std::fmt::Display + BoundingBox + RenderComplexity {}
|
||||
impl Primitive for String {}
|
||||
impl Primitive for bool {}
|
||||
impl Primitive for f32 {}
|
||||
|
||||
Reference in New Issue
Block a user