mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Downscale Images to document resolution (#1077)
* Add DownscaleNode * Add lambda (call argument) input type + fix caching * Add comment explaining Lambda input * Automatically insert cache node after downscale node * Implement sparse hashing of images
This commit is contained in:
committed by
Keavon Chambers
parent
0a775fe9be
commit
fe233504ca
@@ -6,14 +6,18 @@ use graphene_core::Node;
|
||||
|
||||
/// Caches the output of a given Node and acts as a proxy
|
||||
#[derive(Default)]
|
||||
pub struct CacheNode<T> {
|
||||
pub struct CacheNode<T, CachedNode> {
|
||||
// We have to use an append only data structure to make sure the references
|
||||
// to the cache entries are always valid
|
||||
cache: boxcar::Vec<(u64, T)>,
|
||||
node: CachedNode,
|
||||
}
|
||||
impl<'i, T: 'i + Hash> Node<'i, T> for CacheNode<T> {
|
||||
impl<'i, T: 'i, I: 'i + Hash, CachedNode: 'i> Node<'i, I> for CacheNode<T, CachedNode>
|
||||
where
|
||||
CachedNode: for<'any_input> Node<'any_input, I, Output = T>,
|
||||
{
|
||||
type Output = &'i T;
|
||||
fn eval<'s: 'i>(&'s self, input: T) -> Self::Output {
|
||||
fn eval<'s: 'i>(&'s self, input: I) -> Self::Output {
|
||||
let mut hasher = Xxh3::new();
|
||||
input.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
@@ -22,15 +26,16 @@ impl<'i, T: 'i + Hash> Node<'i, T> for CacheNode<T> {
|
||||
return cached_value;
|
||||
} else {
|
||||
trace!("Cache miss");
|
||||
let index = self.cache.push((hash, input));
|
||||
let output = self.node.eval(input);
|
||||
let index = self.cache.push((hash, output));
|
||||
return &self.cache[index].1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> CacheNode<T> {
|
||||
pub fn new() -> CacheNode<T> {
|
||||
CacheNode { cache: boxcar::Vec::new() }
|
||||
impl<T, CachedNode> CacheNode<T, CachedNode> {
|
||||
pub fn new(node: CachedNode) -> CacheNode<T, CachedNode> {
|
||||
CacheNode { cache: boxcar::Vec::new(), node }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,33 @@ pub fn export_image_node<'i, 's: 'i>() -> impl Node<'i, 's, (Image, &'i str), Ou
|
||||
}
|
||||
*/
|
||||
|
||||
pub struct DownscaleNode;
|
||||
|
||||
#[node_macro::node_fn(DownscaleNode)]
|
||||
fn downscale(image_frame: ImageFrame) -> ImageFrame {
|
||||
let target_width = image_frame.transform.transform_vector2((1., 0.).into()).length() as usize;
|
||||
let target_height = image_frame.transform.transform_vector2((0., 1.).into()).length() as usize;
|
||||
|
||||
let mut image = Image {
|
||||
width: target_width as u32,
|
||||
height: target_height as u32,
|
||||
data: Vec::with_capacity(target_width * target_height),
|
||||
};
|
||||
|
||||
let scale_factor = DVec2::new(image_frame.image.width as f64, image_frame.image.height as f64) / DVec2::new(target_width as f64, target_height as f64);
|
||||
for y in 0..target_height {
|
||||
for x in 0..target_width {
|
||||
let pixel = image_frame.sample(DVec2::new(x as f64, y as f64) * scale_factor);
|
||||
image.data.push(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
ImageFrame {
|
||||
image,
|
||||
transform: image_frame.transform,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MapImageNode<MapFn> {
|
||||
map_fn: MapFn,
|
||||
@@ -168,8 +195,8 @@ fn compute_transformed_bounding_box(transform: DAffine2) -> Bbox {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BlendImageNode<background, MapFn> {
|
||||
background: background,
|
||||
pub struct BlendImageNode<Background, MapFn> {
|
||||
background: Background,
|
||||
map_fn: MapFn,
|
||||
}
|
||||
|
||||
@@ -202,7 +229,7 @@ where
|
||||
}
|
||||
|
||||
let dst_pixel = background.get_mut(x as usize, y as usize);
|
||||
let src_pixel = foreground.sample(fg_point.x, fg_point.y);
|
||||
let src_pixel = foreground.sample(fg_point);
|
||||
|
||||
*dst_pixel = map_fn.eval((src_pixel, *dst_pixel));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user