mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 23:08:05 +08:00
Replace Footprint/() call arguments with dynamically-bound Contexts (#2232)
* Implement experimental Context struct and traits * Add Ctx super trait * Checkpoint * Return Any instead of DynAny * Fix send implementation for inputs with lifetimes * Port more nodes * Uncomment nodes * Port more nodes * Port vector nodes * Partial progress (the stuff I'm more sure about) * Partial progress (the stuff that's not compiling and I'm not sure about) * Fix more errors * First pass of fixing errors introduced by rebase * Port wasm application io * Fix brush node types * Add type annotation * Fix warnings and wasm compilation * Change types for Document Node definitions * Improve debugging for footprint not found errors * Forward context in append artboard node * Fix thumbnails * Fix loading most demo artwork * Wrap output type of all nodes in future * Encode futures as part of the type * Fix document node definitions for future types * Remove Clippy warnings * Fix more things * Fix opening demo art with manual composition upgrading * Set correct type for manual composition * Fix brush * Fix tests * Update docs for deps * Fix up some node signature issues * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com> Co-authored-by: hypercube <0hypercube@gmail.com>
This commit is contained in:
committed by
Keavon Chambers
parent
0c1e96b9c6
commit
4ff2bdb04f
@@ -2,10 +2,11 @@ use dyn_any::DynAny;
|
||||
use graphene_core::raster::bbox::Bbox;
|
||||
use graphene_core::raster::image::{ImageFrame, ImageFrameTable};
|
||||
use graphene_core::raster::{
|
||||
Alpha, Bitmap, BitmapMut, CellularDistanceFunction, CellularReturnType, DomainWarpType, FractalType, Image, Linear, LinearChannel, Luminance, NoiseType, Pixel, RGBMut, RedGreenBlue, Sample,
|
||||
Alpha, AlphaMut, Bitmap, BitmapMut, CellularDistanceFunction, CellularReturnType, DomainWarpType, FractalType, Image, Linear, LinearChannel, Luminance, NoiseType, Pixel, RGBMut, RedGreenBlue,
|
||||
Sample,
|
||||
};
|
||||
use graphene_core::transform::{Footprint, Transform};
|
||||
use graphene_core::{AlphaBlending, Color, Node};
|
||||
use graphene_core::transform::Transform;
|
||||
use graphene_core::{AlphaBlending, Color, Ctx, ExtractFootprint, Node};
|
||||
|
||||
use fastnoise_lite;
|
||||
use glam::{DAffine2, DVec2, Vec2};
|
||||
@@ -28,13 +29,14 @@ impl From<std::io::Error> for Error {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug: Raster"))]
|
||||
fn sample_image(footprint: Footprint, image_frame: ImageFrameTable<Color>) -> ImageFrameTable<Color> {
|
||||
fn sample_image(ctx: impl ExtractFootprint + Clone + Send, image_frame: ImageFrameTable<Color>) -> ImageFrameTable<Color> {
|
||||
let image_frame = image_frame.one_item();
|
||||
|
||||
// Resize the image using the image crate
|
||||
let image = &image_frame.image;
|
||||
let data = bytemuck::cast_vec(image.data.clone());
|
||||
|
||||
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 intersection = viewport_bounds.intersect(&image_bounds);
|
||||
@@ -107,15 +109,7 @@ where
|
||||
image
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct InsertChannelNode<P, S, Insertion, TargetChannel> {
|
||||
insertion: Insertion,
|
||||
target_channel: TargetChannel,
|
||||
_p: PhantomData<P>,
|
||||
_s: PhantomData<S>,
|
||||
}
|
||||
|
||||
#[node_macro::old_node_fn(InsertChannelNode<_P, _S>)]
|
||||
#[node_macro::node]
|
||||
fn insert_channel<
|
||||
// _P is the color of the input image.
|
||||
_P: RGBMut,
|
||||
@@ -124,8 +118,9 @@ fn insert_channel<
|
||||
Input: BitmapMut<Pixel = _P>,
|
||||
Insertion: Bitmap<Pixel = _S>,
|
||||
>(
|
||||
mut image: Input,
|
||||
insertion: Insertion,
|
||||
_: impl Ctx,
|
||||
#[implementations(ImageFrameTable<Color>)] mut image: Input,
|
||||
#[implementations(ImageFrameTable<Color>)] insertion: Insertion,
|
||||
target_channel: RedGreenBlue,
|
||||
) -> Input
|
||||
where
|
||||
@@ -154,15 +149,60 @@ where
|
||||
|
||||
image
|
||||
}
|
||||
#[node_macro::node]
|
||||
fn combine_channels<
|
||||
// _P is the color of the input image.
|
||||
_P: RGBMut + AlphaMut,
|
||||
_S: Pixel + Luminance,
|
||||
// Input image
|
||||
Input: BitmapMut<Pixel = _P>,
|
||||
Red: Bitmap<Pixel = _S>,
|
||||
Green: Bitmap<Pixel = _S>,
|
||||
Blue: Bitmap<Pixel = _S>,
|
||||
Alpha: Bitmap<Pixel = _S>,
|
||||
>(
|
||||
_: impl Ctx,
|
||||
#[implementations(ImageFrameTable<Color>)] mut image: Input,
|
||||
#[implementations(ImageFrameTable<Color>)] red: Red,
|
||||
#[implementations(ImageFrameTable<Color>)] green: Green,
|
||||
#[implementations(ImageFrameTable<Color>)] blue: Blue,
|
||||
#[implementations(ImageFrameTable<Color>)] alpha: Alpha,
|
||||
) -> Input
|
||||
where
|
||||
_P::ColorChannel: Linear,
|
||||
{
|
||||
let dimensions = [red.dim(), green.dim(), blue.dim(), alpha.dim()];
|
||||
if dimensions.iter().all(|&(x, _)| x == 0) {
|
||||
return image;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MaskImageNode<P, S, Stencil> {
|
||||
stencil: Stencil,
|
||||
_p: PhantomData<P>,
|
||||
_s: PhantomData<S>,
|
||||
if dimensions.iter().any(|&(x, y)| x != image.width() || y != image.height()) {
|
||||
log::warn!("Stencil and image have different sizes. This is not supported.");
|
||||
return image;
|
||||
}
|
||||
|
||||
for y in 0..image.height() {
|
||||
for x in 0..image.width() {
|
||||
let image_pixel = image.get_pixel_mut(x, y).unwrap();
|
||||
if let Some(r) = red.get_pixel(x, y) {
|
||||
image_pixel.set_red(r.l().cast_linear_channel());
|
||||
}
|
||||
if let Some(g) = green.get_pixel(x, y) {
|
||||
image_pixel.set_green(g.l().cast_linear_channel());
|
||||
}
|
||||
if let Some(b) = blue.get_pixel(x, y) {
|
||||
image_pixel.set_blue(b.l().cast_linear_channel());
|
||||
}
|
||||
if let Some(a) = alpha.get_pixel(x, y) {
|
||||
image_pixel.set_alpha(a.l().cast_linear_channel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image
|
||||
}
|
||||
|
||||
#[node_macro::old_node_fn(MaskImageNode<_P, _S>)]
|
||||
#[node_macro::node()]
|
||||
fn mask_image<
|
||||
// _P is the color of the input image. It must have an alpha channel because that is going to
|
||||
// be modified by the mask
|
||||
@@ -175,8 +215,9 @@ fn mask_image<
|
||||
// Stencil
|
||||
Stencil: Transform + Sample<Pixel = _S>,
|
||||
>(
|
||||
mut image: Input,
|
||||
stencil: Stencil,
|
||||
_: impl Ctx,
|
||||
#[implementations(ImageFrameTable<Color>)] mut image: Input,
|
||||
#[implementations(ImageFrameTable<Color>)] stencil: Stencil,
|
||||
) -> Input {
|
||||
let image_size = DVec2::new(image.width() as f64, image.height() as f64);
|
||||
let mask_size = stencil.transform().decompose_scale();
|
||||
@@ -207,17 +248,17 @@ fn mask_image<
|
||||
image
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BlendImageTupleNode<P, Fg, MapFn> {
|
||||
map_fn: MapFn,
|
||||
_p: PhantomData<P>,
|
||||
_fg: PhantomData<Fg>,
|
||||
}
|
||||
// #[derive(Debug, Clone, Copy)]
|
||||
// pub struct BlendImageTupleNode<P, Fg, MapFn> {
|
||||
// map_fn: MapFn,
|
||||
// _p: PhantomData<P>,
|
||||
// _fg: PhantomData<Fg>,
|
||||
// }
|
||||
|
||||
#[node_macro::old_node_fn(BlendImageTupleNode<_P, _Fg>)]
|
||||
fn blend_image_tuple<_P: Alpha + Pixel + Debug, MapFn, _Fg: Sample<Pixel = _P> + Transform>(images: (ImageFrame<_P>, _Fg), map_fn: &'input MapFn) -> ImageFrame<_P>
|
||||
#[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>
|
||||
where
|
||||
MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'input + Clone,
|
||||
MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'n + Clone,
|
||||
{
|
||||
let (background, foreground) = images;
|
||||
|
||||
@@ -319,7 +360,7 @@ fn extend_image_to_bounds(image: ImageFrame<Color>, bounds: DAffine2) -> ImageFr
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Debug: Raster"))]
|
||||
fn empty_image<P: Pixel>(_: (), transform: DAffine2, #[implementations(Color)] color: P) -> ImageFrame<P> {
|
||||
fn empty_image<P: Pixel>(_: impl Ctx, transform: DAffine2, #[implementations(Color)] color: P) -> ImageFrame<P> {
|
||||
let width = transform.transform_vector2(DVec2::new(1., 0.)).length() as u32;
|
||||
let height = transform.transform_vector2(DVec2::new(0., 1.)).length() as u32;
|
||||
|
||||
@@ -431,10 +472,10 @@ fn empty_image<P: Pixel>(_: (), transform: DAffine2, #[implementations(Color)] c
|
||||
// tiling: Tiling: bool,
|
||||
// }
|
||||
|
||||
#[node_macro::node(category("Raster: Generator"))]
|
||||
#[node_macro::node(category("Raster"))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn noise_pattern(
|
||||
footprint: Footprint,
|
||||
ctx: impl ExtractFootprint + Ctx,
|
||||
_primary: (),
|
||||
clip: bool,
|
||||
seed: u32,
|
||||
@@ -452,6 +493,7 @@ fn noise_pattern(
|
||||
cellular_return_type: CellularReturnType,
|
||||
cellular_jitter: f64,
|
||||
) -> ImageFrameTable<Color> {
|
||||
let footprint = ctx.footprint();
|
||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
||||
|
||||
let mut size = viewport_bounds.size();
|
||||
@@ -585,8 +627,9 @@ fn noise_pattern(
|
||||
ImageFrameTable::new(result)
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster: Generator"))]
|
||||
fn mandelbrot(footprint: Footprint) -> ImageFrameTable<Color> {
|
||||
#[node_macro::node(category("Raster"))]
|
||||
fn mandelbrot(ctx: impl ExtractFootprint + Send) -> ImageFrameTable<Color> {
|
||||
let footprint = ctx.footprint();
|
||||
let viewport_bounds = footprint.viewport_bounds_in_local_space();
|
||||
|
||||
let image_bounds = Bbox::from_transform(DAffine2::IDENTITY).to_axis_aligned_bbox();
|
||||
|
||||
Reference in New Issue
Block a user