Implement the Brush without relying on a stamp texture

Test Plan: Test the BrushNode in the editor

Reviewers: Keavon

Reviewed By: Keavon

Pull Request: https://github.com/GraphiteEditor/Graphite/pull/1184
This commit is contained in:
Dennis Kobert
2023-04-29 01:31:14 +02:00
committed by Keavon Chambers
parent 5d9c0cb4d5
commit 1020eb6835
31 changed files with 221 additions and 178 deletions

View File

@@ -13,7 +13,7 @@ pub struct PushConstants {
impl Sample for SampledImage<Image2d> {
type Pixel = Color;
fn sample(&self, pos: glam::DVec2) -> Option<Self::Pixel> {
fn sample(&self, pos: glam::DVec2, _area: glam::DVec2) -> Option<Self::Pixel> {
let color = self.sample(pos);
Color::from_rgbaf32(color.x, color.y, color.z, color.w)
}

View File

@@ -252,7 +252,7 @@ mod test {
let value: ClonedNode<Result<&u32, ()>> = ClonedNode(Ok(&4u32));
assert_eq!(value.eval(()), Ok(&4u32));
//let type_erased_clone = clone as &dyn for<'a> Node<'a, &'a u32, Output = u32>;
let map_result = MapResultNode::new(ValueNode::new(FnNode::new(|x: &u32| x.clone())));
let map_result = MapResultNode::new(ValueNode::new(FnNode::new(|x: &u32| *x)));
//et type_erased = &map_result as &dyn for<'a> Node<'a, Result<&'a u32, ()>, Output = Result<u32, ()>>;
assert_eq!(map_result.eval(Ok(&4u32)), Ok(4u32));
let fst = value.then(map_result);

View File

@@ -171,7 +171,7 @@ pub trait Luminance {
pub trait Sample {
type Pixel: Pixel;
// TODO: Add an area parameter
fn sample(&self, pos: DVec2) -> Option<Self::Pixel>;
fn sample(&self, pos: DVec2, area: DVec2) -> Option<Self::Pixel>;
}
// TODO: We might rename this to Bitmap at some point

View File

@@ -715,6 +715,9 @@ impl Color {
}
pub fn to_unassociated_alpha(&self) -> Self {
if self.alpha == 0. {
return *self;
}
let unmultiply = 1. / self.alpha;
Self {
red: self.red * unmultiply,

View File

@@ -26,7 +26,7 @@ mod base64_serde {
{
use serde::de::Error;
let color_from_chunk = |chunk: &[u8]| P::from_bytes(chunk.try_into().unwrap()).clone();
let color_from_chunk = |chunk: &[u8]| P::from_bytes(chunk.try_into().unwrap());
let colors_from_bytes = |bytes: Vec<u8>| bytes.chunks_exact(P::byte_size()).map(color_from_chunk).collect();
@@ -129,7 +129,7 @@ where
pub fn into_flat_u8(self) -> (Vec<u8>, u32, u32) {
let Image { width, height, data } = self;
let to_gamma = |x| SRGBGammaFloat::from_linear(x);
let to_gamma = SRGBGammaFloat::from_linear;
let to_u8 = |x| (num_cast::<_, f32>(x).unwrap() * 255.) as u8;
let result_bytes = data
@@ -201,7 +201,8 @@ pub struct ImageFrame<P: Pixel> {
impl<P: Debug + Copy + Pixel> Sample for ImageFrame<P> {
type Pixel = P;
fn sample(&self, pos: DVec2) -> Option<Self::Pixel> {
// TODO: Improve sampling logic
fn sample(&self, pos: DVec2, _area: DVec2) -> Option<Self::Pixel> {
let image_size = DVec2::new(self.image.width() as f64, self.image.height() as f64);
let pos = (DAffine2::from_scale(image_size) * self.transform.inverse()).transform_point2(pos);
if pos.x < 0. || pos.y < 0. || pos.x >= image_size.x || pos.y >= image_size.y {

View File

@@ -81,7 +81,7 @@ mod test {
fn test_ref_eval() {
let value = ValueNode::new(5);
assert_eq!((&value).eval(()), &5);
assert_eq!(value.eval(()), &5);
let id = IdNode::new();
let compose = ComposeNode::new(&value, &id);

View File

@@ -7,6 +7,7 @@ pub struct IntNode<const N: u32>;
impl<'i, const N: u32> Node<'i, ()> for IntNode<N> {
type Output = u32;
#[inline(always)]
fn eval(&'i self, _input: ()) -> Self::Output {
N
}
@@ -17,6 +18,7 @@ pub struct ValueNode<T>(pub T);
impl<'i, T: 'i> Node<'i, ()> for ValueNode<T> {
type Output = &'i T;
#[inline(always)]
fn eval(&'i self, _input: ()) -> Self::Output {
&self.0
}
@@ -45,6 +47,7 @@ pub struct ClonedNode<T: Clone>(pub T);
impl<'i, T: Clone + 'i> Node<'i, ()> for ClonedNode<T> {
type Output = T;
#[inline(always)]
fn eval(&'i self, _input: ()) -> Self::Output {
self.0.clone()
}
@@ -62,11 +65,34 @@ impl<T: Clone> From<T> for ClonedNode<T> {
}
}
#[derive(Clone, Copy)]
/// The DebugClonedNode logs every time it is evaluated.
/// This is useful for debugging.
pub struct DebugClonedNode<T: Clone>(pub T);
impl<'i, T: Clone + 'i> Node<'i, ()> for DebugClonedNode<T> {
type Output = T;
#[inline(always)]
fn eval(&'i self, _input: ()) -> Self::Output {
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
log::debug!("DebugClonedNode::eval");
self.0.clone()
}
}
impl<T: Clone> DebugClonedNode<T> {
pub const fn new(value: T) -> ClonedNode<T> {
ClonedNode(value)
}
}
#[derive(Clone, Copy)]
pub struct CopiedNode<T: Copy>(pub T);
impl<'i, T: Copy + 'i> Node<'i, ()> for CopiedNode<T> {
type Output = T;
#[inline(always)]
fn eval(&'i self, _input: ()) -> Self::Output {
self.0
}

View File

@@ -26,7 +26,7 @@ fn set_vector_data_fill(
positions: Vec<(f64, Option<Color>)>,
) -> VectorData {
vector_data.style.set_fill(match fill_type {
FillType::None | FillType::Solid => solid_color.map_or(Fill::None, |solid_color| Fill::Solid(solid_color)),
FillType::None | FillType::Solid => solid_color.map_or(Fill::None, Fill::Solid),
FillType::Gradient => Fill::Gradient(Gradient {
start,
end,