Insert pasted images as layers in document graph (#1418)

Changes:

Graph is evaluated on every viewport change
move all navigation logic into the viewport
reduce the number of js roundtrips
add canvas rendering
enable image pasting
various cleanups
Fix cache nodes being reset every evaluation
This commit is contained in:
Dennis Kobert
2023-09-13 17:02:35 +02:00
committed by Keavon Chambers
parent d82f133514
commit 833f41bccb
20 changed files with 202 additions and 226 deletions

View File

@@ -58,41 +58,49 @@ fn buffer_node<R: std::io::Read>(reader: R) -> Result<Vec<u8>, Error> {
Ok(std::io::Read::bytes(reader).collect::<Result<Vec<_>, _>>()?)
}
pub struct DownresNode<ImageFrame> {
pub struct SampleNode<ImageFrame> {
image_frame: ImageFrame,
}
#[node_macro::node_fn(DownresNode)]
fn downres(footprint: Footprint, image_frame: ImageFrame<Color>) -> ImageFrame<Color> {
#[node_macro::node_fn(SampleNode)]
fn sample(footprint: Footprint, image_frame: ImageFrame<Color>) -> ImageFrame<Color> {
// resize the image using the image crate
let image = image_frame.image;
let data = bytemuck::cast_vec(image.data);
let viewport_bounds = footprint.viewport_bounds_in_local_space();
log::debug!("viewport_bounds: {viewport_bounds:?}");
let bbox = Bbox::from_transform(image_frame.transform * DAffine2::from_scale(DVec2::new(image.width as f64, image.height as f64)));
log::debug!("local_bounds: {bbox:?}");
let bounds = viewport_bounds.intersect(&bbox.to_axis_aligned_bbox());
log::debug!("intersection: {bounds:?}");
let union = viewport_bounds.union(&bbox.to_axis_aligned_bbox());
log::debug!("union: {union:?}");
let size = bounds.size();
let image_bounds = Bbox::from_transform(image_frame.transform).to_axis_aligned_bbox();
let intersection = viewport_bounds.intersect(&image_bounds);
let image_size = DAffine2::from_scale(DVec2::new(image.width as f64, image.height as f64));
let size = intersection.size();
let size_px = image_size.transform_vector2(size).as_uvec2();
// If the image would not be visible, return an empty image
if size.x <= 0. || size.y <= 0. {
return ImageFrame::empty();
}
let image_buffer = image::Rgba32FImage::from_raw(image.width, image.height, data).expect("Failed to convert internal ImageFrame into image-rs data type.");
let dynamic_image: image::DynamicImage = image_buffer.into();
let offset = (bounds.start - viewport_bounds.start).as_uvec2();
let cropped = dynamic_image.crop_imm(offset.x, offset.y, size.x as u32, size.y as u32);
let offset = (intersection.start - image_bounds.start).max(DVec2::ZERO);
let offset_px = image_size.transform_vector2(offset).as_uvec2();
let cropped = dynamic_image.crop_imm(offset_px.x, offset_px.y, size_px.x, size_px.y);
log::debug!("transform: {:?}", footprint.transform);
log::debug!("size: {size:?}");
let viewport_resolution_x = footprint.transform.transform_vector2(DVec2::X * size.x).length();
let viewport_resolution_y = footprint.transform.transform_vector2(DVec2::Y * size.y).length();
let nwidth = viewport_resolution_x as u32;
let nheight = viewport_resolution_y as u32;
log::debug!("x: {viewport_resolution_x}, y: {viewport_resolution_y}");
let mut nwidth = size_px.x;
let mut nheight = size_px.y;
let resized = cropped.resize_exact(nwidth, nheight, image::imageops::Lanczos3);
// Only downscale the image for now
let resized = if nwidth < image.width || nheight < image.height {
nwidth = viewport_resolution_x as u32;
nheight = viewport_resolution_y as u32;
// TODO: choose filter based on quality reqirements
cropped.resize_exact(nwidth, nheight, image::imageops::Triangle)
} else {
cropped
};
let buffer = resized.to_rgba32f();
let buffer = buffer.into_raw();
let vec = bytemuck::cast_vec(buffer);
@@ -101,11 +109,10 @@ fn downres(footprint: Footprint, image_frame: ImageFrame<Color>) -> ImageFrame<C
height: nheight,
data: vec,
};
// we need to adjust the offset if we truncate the offset calculation
ImageFrame {
image,
transform: image_frame.transform,
}
let new_transform = image_frame.transform * DAffine2::from_translation(offset) * DAffine2::from_scale(DVec2::new(size.x, size.y));
ImageFrame { image, transform: new_transform }
}
#[derive(Debug, Clone, Copy)]