|
use crate::brush_stroke::BrushStroke; |
|
use crate::brush_stroke::BrushStyle; |
|
use dyn_any::DynAny; |
|
use graphene_core::instances::Instance; |
|
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; |
|
|
|
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)] |
|
struct BrushCacheImpl { |
|
|
|
prev_input: Vec<BrushStroke>, |
|
|
|
|
|
#[serde(deserialize_with = "graphene_core::raster::image::migrate_image_frame_instance")] |
|
background: Instance<Raster<CPU>>, |
|
#[serde(deserialize_with = "graphene_core::raster::image::migrate_image_frame_instance")] |
|
blended_image: Instance<Raster<CPU>>, |
|
#[serde(deserialize_with = "graphene_core::raster::image::migrate_image_frame_instance")] |
|
last_stroke_texture: Instance<Raster<CPU>>, |
|
|
|
|
|
#[serde(skip)] |
|
brush_texture_cache: HashMap<BrushStyle, Raster<CPU>>, |
|
} |
|
|
|
impl BrushCacheImpl { |
|
fn compute_brush_plan(&mut self, mut background: Instance<Raster<CPU>>, input: &[BrushStroke]) -> BrushPlan { |
|
|
|
if background != self.background { |
|
self.background = background.clone(); |
|
return BrushPlan { |
|
strokes: input.to_vec(), |
|
background, |
|
..Default::default() |
|
}; |
|
} |
|
|
|
|
|
let blended_strokes = &self.prev_input[..self.prev_input.len().saturating_sub(1)]; |
|
let num_blended_strokes = blended_strokes.len(); |
|
if input.get(..num_blended_strokes) != Some(blended_strokes) { |
|
return BrushPlan { |
|
strokes: input.to_vec(), |
|
background, |
|
..Default::default() |
|
}; |
|
} |
|
|
|
|
|
|
|
background = std::mem::take(&mut self.blended_image); |
|
|
|
|
|
let mut first_stroke_texture = Instance { |
|
instance: Raster::<CPU>::default(), |
|
transform: glam::DAffine2::ZERO, |
|
..Default::default() |
|
}; |
|
let mut first_stroke_point_skip = 0; |
|
let strokes = input[num_blended_strokes..].to_vec(); |
|
if !strokes.is_empty() && self.prev_input.len() > num_blended_strokes { |
|
let last_stroke = &self.prev_input[num_blended_strokes]; |
|
let same_style = strokes[0].style == last_stroke.style; |
|
let prev_points = last_stroke.compute_blit_points(); |
|
let new_points = strokes[0].compute_blit_points(); |
|
let is_point_prefix = new_points.get(..prev_points.len()) == Some(&prev_points); |
|
if same_style && is_point_prefix { |
|
first_stroke_texture = std::mem::take(&mut self.last_stroke_texture); |
|
first_stroke_point_skip = prev_points.len(); |
|
} |
|
} |
|
|
|
self.prev_input = Vec::new(); |
|
BrushPlan { |
|
strokes, |
|
background, |
|
first_stroke_texture, |
|
first_stroke_point_skip, |
|
} |
|
} |
|
|
|
pub fn cache_results(&mut self, input: Vec<BrushStroke>, blended_image: Instance<Raster<CPU>>, last_stroke_texture: Instance<Raster<CPU>>) { |
|
self.prev_input = input; |
|
self.blended_image = blended_image; |
|
self.last_stroke_texture = last_stroke_texture; |
|
} |
|
} |
|
|
|
impl Hash for BrushCacheImpl { |
|
|
|
fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {} |
|
} |
|
|
|
#[derive(Clone, Debug, Default)] |
|
pub struct BrushPlan { |
|
pub strokes: Vec<BrushStroke>, |
|
pub background: Instance<Raster<CPU>>, |
|
pub first_stroke_texture: Instance<Raster<CPU>>, |
|
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() |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
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, |
|
} |
|
} |
|
} |
|
} |
|
|
|
impl PartialEq for BrushCache { |
|
fn eq(&self, other: &Self) -> bool { |
|
if Arc::ptr_eq(&self.inner, &other.inner) { |
|
return true; |
|
} |
|
|
|
let s = self.inner.lock().unwrap(); |
|
let o = other.inner.lock().unwrap(); |
|
|
|
*s == *o |
|
} |
|
} |
|
|
|
impl Hash for BrushCache { |
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
|
self.inner.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(); |
|
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(); |
|
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(); |
|
inner.brush_texture_cache.get(style).cloned() |
|
} |
|
|
|
pub fn store_brush(&self, style: BrushStyle, brush: Raster<CPU>) { |
|
let mut inner = self.inner.lock().unwrap(); |
|
inner.brush_texture_cache.insert(style, brush); |
|
} |
|
} |
|
|