mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-19 19:08:05 +08:00
Instance tables refactor part 2: move the transform and alpha_blending fields up a level (#2249)
* Fix domain data structure field plural naming * Rename method one_item to one_instance Rename method one_item to one_instance * Move the Instance<T> methods over to providing an Instance<T>/InstanceMut<T> Move the Instance<T> methods over to providing an Instance<T>/InstanceMut<T> * Add transform and alpha_blending fields to Instances<T> * Finish the refactor (Brush tool is broken though) * Add test for brush node * Fix brush node * Fix default empty images being 1x1 instead of 0x0 as they should be * Fix tests * Fix path transform * Add correct upgrading to move the transform/blending up a level --------- Co-authored-by: hypercube <0hypercube@gmail.com>
This commit is contained in:
@@ -5,8 +5,8 @@ use graphene_core::raster::{
|
||||
Alpha, AlphaMut, Bitmap, BitmapMut, CellularDistanceFunction, CellularReturnType, DomainWarpType, FractalType, Image, Linear, LinearChannel, Luminance, NoiseType, Pixel, RGBMut, RedGreenBlue,
|
||||
Sample,
|
||||
};
|
||||
use graphene_core::transform::Transform;
|
||||
use graphene_core::{AlphaBlending, Color, Ctx, ExtractFootprint, Node};
|
||||
use graphene_core::transform::{Transform, TransformMut};
|
||||
use graphene_core::{AlphaBlending, Color, Ctx, ExtractFootprint, GraphicElement, Node};
|
||||
|
||||
use fastnoise_lite;
|
||||
use glam::{DAffine2, DVec2, Vec2};
|
||||
@@ -30,7 +30,10 @@ impl From<std::io::Error> for Error {
|
||||
|
||||
#[node_macro::node(category("Debug: Raster"))]
|
||||
fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFrameTable<Color>) -> ImageFrameTable<Color> {
|
||||
let image_frame = image_frame.one_item();
|
||||
let image_frame_transform = image_frame.transform();
|
||||
let image_frame_alpha_blending = image_frame.one_instance().alpha_blending;
|
||||
|
||||
let image_frame = image_frame.one_instance().instance;
|
||||
|
||||
// Resize the image using the image crate
|
||||
let image = &image_frame.image;
|
||||
@@ -38,7 +41,7 @@ fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFra
|
||||
|
||||
let footprint = ctx.footprint();
|
||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
||||
let image_bounds = Bbox::from_transform(image_frame.transform).to_axis_aligned_bbox();
|
||||
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();
|
||||
@@ -46,7 +49,7 @@ fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFra
|
||||
|
||||
// If the image would not be visible, return an empty image
|
||||
if size.x <= 0. || size.y <= 0. {
|
||||
return ImageFrameTable::default();
|
||||
return ImageFrameTable::empty();
|
||||
}
|
||||
|
||||
let image_buffer = image::Rgba32FImage::from_raw(image.width, image.height, data).expect("Failed to convert internal image format into image-rs data type.");
|
||||
@@ -81,15 +84,13 @@ fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFra
|
||||
};
|
||||
// we need to adjust the offset if we truncate the offset calculation
|
||||
|
||||
let new_transform = image_frame.transform * DAffine2::from_translation(offset) * DAffine2::from_scale(size);
|
||||
let new_transform = image_frame_transform * DAffine2::from_translation(offset) * DAffine2::from_scale(size);
|
||||
|
||||
let result = ImageFrame {
|
||||
image,
|
||||
transform: new_transform,
|
||||
alpha_blending: image_frame.alpha_blending,
|
||||
};
|
||||
let mut result = ImageFrameTable::new(ImageFrame { image });
|
||||
*result.transform_mut() = new_transform;
|
||||
*result.one_instance_mut().alpha_blending = *image_frame_alpha_blending;
|
||||
|
||||
ImageFrameTable::new(result)
|
||||
result
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -256,33 +257,35 @@ fn mask_image<
|
||||
// }
|
||||
|
||||
#[node_macro::node(skip_impl)]
|
||||
async fn blend_image_tuple<_P: Alpha + Pixel + Debug + Send, MapFn, _Fg: Sample<Pixel = _P> + Transform + Clone + Send + 'n>(images: (ImageFrame<_P>, _Fg), map_fn: &'n MapFn) -> ImageFrame<_P>
|
||||
async fn blend_image_tuple<_P, MapFn, _Fg>(images: (ImageFrameTable<_P>, _Fg), map_fn: &'n MapFn) -> ImageFrameTable<_P>
|
||||
where
|
||||
_P: Alpha + Pixel + Debug + Send + dyn_any::StaticType,
|
||||
_P::Static: Pixel,
|
||||
MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'n + Clone,
|
||||
_Fg: Sample<Pixel = _P> + Transform + Clone + Send + 'n,
|
||||
GraphicElement: From<ImageFrame<_P>>,
|
||||
{
|
||||
let (background, foreground) = images;
|
||||
|
||||
blend_image(foreground, background, map_fn)
|
||||
}
|
||||
|
||||
fn blend_image<'input, _P: Alpha + Pixel + Debug, MapFn, Frame: Sample<Pixel = _P> + Transform, Background: BitmapMut<Pixel = _P> + Transform + Sample<Pixel = _P>>(
|
||||
foreground: Frame,
|
||||
background: Background,
|
||||
map_fn: &'input MapFn,
|
||||
) -> Background
|
||||
fn blend_image<'input, _P, MapFn, Frame, Background>(foreground: Frame, background: Background, map_fn: &'input MapFn) -> Background
|
||||
where
|
||||
MapFn: Node<'input, (_P, _P), Output = _P>,
|
||||
_P: Pixel + Alpha + Debug,
|
||||
Frame: Sample<Pixel = _P> + Transform,
|
||||
Background: BitmapMut<Pixel = _P> + Sample<Pixel = _P> + Transform,
|
||||
{
|
||||
blend_image_closure(foreground, background, |a, b| map_fn.eval((a, b)))
|
||||
}
|
||||
|
||||
pub fn blend_image_closure<_P: Alpha + Pixel + Debug, MapFn, Frame: Sample<Pixel = _P> + Transform, Background: BitmapMut<Pixel = _P> + Transform + Sample<Pixel = _P>>(
|
||||
foreground: Frame,
|
||||
mut background: Background,
|
||||
map_fn: MapFn,
|
||||
) -> Background
|
||||
pub fn blend_image_closure<_P, MapFn, Frame, Background>(foreground: Frame, mut background: Background, map_fn: MapFn) -> Background
|
||||
where
|
||||
MapFn: Fn(_P, _P) -> _P,
|
||||
_P: Pixel + Alpha + Debug,
|
||||
Frame: Sample<Pixel = _P> + Transform,
|
||||
Background: BitmapMut<Pixel = _P> + Sample<Pixel = _P> + Transform,
|
||||
{
|
||||
let background_size = DVec2::new(background.width() as f64, background.height() as f64);
|
||||
|
||||
@@ -319,19 +322,20 @@ pub struct ExtendImageToBoundsNode<Bounds> {
|
||||
}
|
||||
|
||||
#[node_macro::old_node_fn(ExtendImageToBoundsNode)]
|
||||
fn extend_image_to_bounds(image: ImageFrame<Color>, bounds: DAffine2) -> ImageFrame<Color> {
|
||||
fn extend_image_to_bounds(image: ImageFrameTable<Color>, bounds: DAffine2) -> ImageFrameTable<Color> {
|
||||
let image_aabb = Bbox::unit().affine_transform(image.transform()).to_axis_aligned_bbox();
|
||||
let bounds_aabb = Bbox::unit().affine_transform(bounds.transform()).to_axis_aligned_bbox();
|
||||
if image_aabb.contains(bounds_aabb.start) && image_aabb.contains(bounds_aabb.end) {
|
||||
return image;
|
||||
}
|
||||
|
||||
if image.image.width == 0 || image.image.height == 0 {
|
||||
let image_instance = image.one_instance().instance;
|
||||
if image_instance.image.width == 0 || image_instance.image.height == 0 {
|
||||
return empty_image((), bounds, Color::TRANSPARENT);
|
||||
}
|
||||
|
||||
let orig_image_scale = DVec2::new(image.image.width as f64, image.image.height as f64);
|
||||
let layer_to_image_space = DAffine2::from_scale(orig_image_scale) * image.transform.inverse();
|
||||
let orig_image_scale = DVec2::new(image_instance.image.width as f64, image_instance.image.height as f64);
|
||||
let layer_to_image_space = DAffine2::from_scale(orig_image_scale) * image.transform().inverse();
|
||||
let bounds_in_image_space = Bbox::unit().affine_transform(layer_to_image_space * bounds).to_axis_aligned_bbox();
|
||||
|
||||
let new_start = bounds_in_image_space.start.floor().min(DVec2::ZERO);
|
||||
@@ -341,36 +345,37 @@ fn extend_image_to_bounds(image: ImageFrame<Color>, bounds: DAffine2) -> ImageFr
|
||||
// Copy over original image into enlarged image.
|
||||
let mut new_img = Image::new(new_scale.x as u32, new_scale.y as u32, Color::TRANSPARENT);
|
||||
let offset_in_new_image = (-new_start).as_uvec2();
|
||||
for y in 0..image.image.height {
|
||||
let old_start = y * image.image.width;
|
||||
for y in 0..image_instance.image.height {
|
||||
let old_start = y * image_instance.image.width;
|
||||
let new_start = (y + offset_in_new_image.y) * new_img.width + offset_in_new_image.x;
|
||||
let old_row = &image.image.data[old_start as usize..(old_start + image.image.width) as usize];
|
||||
let new_row = &mut new_img.data[new_start as usize..(new_start + image.image.width) as usize];
|
||||
let old_row = &image_instance.image.data[old_start as usize..(old_start + image_instance.image.width) as usize];
|
||||
let new_row = &mut new_img.data[new_start as usize..(new_start + image_instance.image.width) as usize];
|
||||
new_row.copy_from_slice(old_row);
|
||||
}
|
||||
|
||||
// Compute new transform.
|
||||
// let layer_to_new_texture_space = (DAffine2::from_scale(1. / new_scale) * DAffine2::from_translation(new_start) * layer_to_image_space).inverse();
|
||||
let new_texture_to_layer_space = image.transform * DAffine2::from_scale(1. / orig_image_scale) * DAffine2::from_translation(new_start) * DAffine2::from_scale(new_scale);
|
||||
ImageFrame {
|
||||
image: new_img,
|
||||
transform: new_texture_to_layer_space,
|
||||
alpha_blending: image.alpha_blending,
|
||||
}
|
||||
let new_texture_to_layer_space = image.transform() * DAffine2::from_scale(1. / orig_image_scale) * DAffine2::from_translation(new_start) * DAffine2::from_scale(new_scale);
|
||||
|
||||
let mut result = ImageFrameTable::new(ImageFrame { image: new_img });
|
||||
*result.transform_mut() = new_texture_to_layer_space;
|
||||
*result.one_instance_mut().alpha_blending = *image.one_instance().alpha_blending;
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug: Raster"))]
|
||||
fn empty_image<P: Pixel>(_: impl Ctx, transform: DAffine2, #[implementations(Color)] color: P) -> ImageFrame<P> {
|
||||
fn empty_image(_: impl Ctx, transform: DAffine2, color: Color) -> ImageFrameTable<Color> {
|
||||
let width = transform.transform_vector2(DVec2::new(1., 0.)).length() as u32;
|
||||
let height = transform.transform_vector2(DVec2::new(0., 1.)).length() as u32;
|
||||
|
||||
let image = Image::new(width, height, color);
|
||||
|
||||
ImageFrame {
|
||||
image,
|
||||
transform,
|
||||
alpha_blending: AlphaBlending::default(),
|
||||
}
|
||||
let mut result = ImageFrameTable::new(ImageFrame { image });
|
||||
*result.transform_mut() = transform;
|
||||
*result.one_instance_mut().alpha_blending = AlphaBlending::default();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
// #[cfg(feature = "serde")]
|
||||
@@ -510,7 +515,7 @@ fn noise_pattern(
|
||||
|
||||
// If the image would not be visible, return an empty image
|
||||
if size.x <= 0. || size.y <= 0. {
|
||||
return ImageFrameTable::default();
|
||||
return ImageFrameTable::empty();
|
||||
}
|
||||
|
||||
let footprint_scale = footprint.scale();
|
||||
@@ -554,13 +559,11 @@ fn noise_pattern(
|
||||
}
|
||||
}
|
||||
|
||||
let result = ImageFrame {
|
||||
image,
|
||||
transform: DAffine2::from_translation(offset) * DAffine2::from_scale(size),
|
||||
alpha_blending: AlphaBlending::default(),
|
||||
};
|
||||
let mut result = ImageFrameTable::new(ImageFrame { image });
|
||||
*result.transform_mut() = DAffine2::from_translation(offset) * DAffine2::from_scale(size);
|
||||
*result.one_instance_mut().alpha_blending = AlphaBlending::default();
|
||||
|
||||
return ImageFrameTable::new(result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
noise.set_noise_type(Some(noise_type));
|
||||
@@ -618,13 +621,11 @@ fn noise_pattern(
|
||||
}
|
||||
}
|
||||
|
||||
let result = ImageFrame {
|
||||
image,
|
||||
transform: DAffine2::from_translation(offset) * DAffine2::from_scale(size),
|
||||
alpha_blending: AlphaBlending::default(),
|
||||
};
|
||||
let mut result = ImageFrameTable::new(ImageFrame { image });
|
||||
*result.transform_mut() = DAffine2::from_translation(offset) * DAffine2::from_scale(size);
|
||||
*result.one_instance_mut().alpha_blending = AlphaBlending::default();
|
||||
|
||||
ImageFrameTable::new(result)
|
||||
result
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster"))]
|
||||
@@ -640,7 +641,7 @@ fn mandelbrot(ctx: impl ExtractFootprint + Send) -> ImageFrameTable<Color> {
|
||||
|
||||
// If the image would not be visible, return an empty image
|
||||
if size.x <= 0. || size.y <= 0. {
|
||||
return ImageFrameTable::default();
|
||||
return ImageFrameTable::empty();
|
||||
}
|
||||
|
||||
let scale = footprint.scale();
|
||||
@@ -662,18 +663,17 @@ fn mandelbrot(ctx: impl ExtractFootprint + Send) -> ImageFrameTable<Color> {
|
||||
}
|
||||
}
|
||||
|
||||
let result = ImageFrame {
|
||||
image: Image {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
..Default::default()
|
||||
},
|
||||
transform: DAffine2::from_translation(offset) * DAffine2::from_scale(size),
|
||||
alpha_blending: Default::default(),
|
||||
let image = Image {
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
..Default::default()
|
||||
};
|
||||
let mut result = ImageFrameTable::new(ImageFrame { image });
|
||||
*result.transform_mut() = DAffine2::from_translation(offset) * DAffine2::from_scale(size);
|
||||
*result.one_instance_mut().alpha_blending = Default::default();
|
||||
|
||||
ImageFrameTable::new(result)
|
||||
result
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
Reference in New Issue
Block a user