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
@@ -6,9 +6,9 @@ use crate::raster::curve::{Curve, CurveManipulatorGroup, ValueMapperNode};
|
||||
use crate::raster::image::{ImageFrame, ImageFrameTable};
|
||||
use crate::raster::{Channel, Color, Pixel};
|
||||
use crate::registry::types::{Angle, Percentage, SignedPercentage};
|
||||
use crate::transform::Footprint;
|
||||
use crate::vector::style::GradientStops;
|
||||
use crate::vector::VectorDataTable;
|
||||
use crate::{Ctx, Node};
|
||||
use crate::{GraphicElement, GraphicGroupTable};
|
||||
|
||||
use dyn_any::DynAny;
|
||||
@@ -284,26 +284,16 @@ impl From<BlendMode> for vello::peniko::Mix {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
async fn luminance<F: 'n + Send, T: Adjust<Color>>(
|
||||
fn luminance<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
input: impl Node<F, Output = T>,
|
||||
mut input: T,
|
||||
luminance_calc: LuminanceCalculation,
|
||||
) -> T {
|
||||
let mut input = input.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
let luminance = match luminance_calc {
|
||||
LuminanceCalculation::SRGB => color.luminance_srgb(),
|
||||
@@ -318,26 +308,16 @@ async fn luminance<F: 'n + Send, T: Adjust<Color>>(
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster"))]
|
||||
async fn extract_channel<F: 'n + Send, T: Adjust<Color>>(
|
||||
fn extract_channel<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
input: impl Node<F, Output = T>,
|
||||
mut input: T,
|
||||
channel: RedGreenBlueAlpha,
|
||||
) -> T {
|
||||
let mut input = input.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
let extracted_value = match channel {
|
||||
RedGreenBlueAlpha::Red => color.r(),
|
||||
@@ -351,25 +331,15 @@ async fn extract_channel<F: 'n + Send, T: Adjust<Color>>(
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster"))]
|
||||
async fn make_opaque<F: 'n + Send, T: Adjust<Color>>(
|
||||
fn make_opaque<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
input: impl Node<F, Output = T>,
|
||||
mut input: T,
|
||||
) -> T {
|
||||
let mut input = input.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
if color.a() == 0. {
|
||||
return color.with_alpha(1.);
|
||||
@@ -385,31 +355,21 @@ async fn make_opaque<F: 'n + Send, T: Adjust<Color>>(
|
||||
// Algorithm from:
|
||||
// https://stackoverflow.com/questions/39510072/algorithm-for-adjustment-of-image-levels
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
async fn levels<F: 'n + Send, T: Adjust<Color>>(
|
||||
fn levels<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
image: impl Node<F, Output = T>,
|
||||
mut image: T,
|
||||
#[default(0.)] shadows: Percentage,
|
||||
#[default(50.)] midtones: Percentage,
|
||||
#[default(100.)] highlights: Percentage,
|
||||
#[default(0.)] output_minimums: Percentage,
|
||||
#[default(100.)] output_maximums: Percentage,
|
||||
) -> T {
|
||||
let mut input = image.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
image.adjust(|color| {
|
||||
let color = color.to_gamma_srgb();
|
||||
|
||||
// Input Range (Range: 0-1)
|
||||
@@ -451,7 +411,7 @@ async fn levels<F: 'n + Send, T: Adjust<Color>>(
|
||||
|
||||
color.to_linear_srgb()
|
||||
});
|
||||
input
|
||||
image
|
||||
}
|
||||
|
||||
// Aims for interoperable compatibility with:
|
||||
@@ -462,23 +422,14 @@ async fn levels<F: 'n + Send, T: Adjust<Color>>(
|
||||
// https://stackoverflow.com/a/55233732/775283
|
||||
// Works the same for gamma and linear color
|
||||
#[node_macro::node(name("Black & White"), category("Raster: Adjustment"))]
|
||||
async fn black_and_white<F: 'n + Send, T: Adjust<Color>>(
|
||||
async fn black_and_white<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
image: impl Node<F, Output = T>,
|
||||
mut image: T,
|
||||
#[default(Color::BLACK)] tint: Color,
|
||||
#[default(40.)]
|
||||
#[range((-200., 300.))]
|
||||
@@ -499,8 +450,7 @@ async fn black_and_white<F: 'n + Send, T: Adjust<Color>>(
|
||||
#[range((-200., 300.))]
|
||||
magentas: Percentage,
|
||||
) -> T {
|
||||
let mut input = image.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
image.adjust(|color| {
|
||||
let color = color.to_gamma_srgb();
|
||||
|
||||
let reds = reds as f32 / 100.;
|
||||
@@ -537,35 +487,25 @@ async fn black_and_white<F: 'n + Send, T: Adjust<Color>>(
|
||||
|
||||
color.to_linear_srgb()
|
||||
});
|
||||
input
|
||||
image
|
||||
}
|
||||
|
||||
// Aims for interoperable compatibility with:
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27hue%20%27%20%3D%20Old,saturation%2C%20Photoshop%205.0
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=0%20%3D%20Use%20other.-,Hue/Saturation,-Hue/Saturation%20settings
|
||||
#[node_macro::node(name("Hue/Saturation"), category("Raster: Adjustment"))]
|
||||
async fn hue_saturation<F: 'n + Send, T: Adjust<Color>>(
|
||||
async fn hue_saturation<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
input: impl Node<F, Output = T>,
|
||||
mut input: T,
|
||||
hue_shift: Angle,
|
||||
saturation_shift: SignedPercentage,
|
||||
lightness_shift: SignedPercentage,
|
||||
) -> T {
|
||||
let mut input = input.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
let color = color.to_gamma_srgb();
|
||||
|
||||
@@ -588,25 +528,15 @@ async fn hue_saturation<F: 'n + Send, T: Adjust<Color>>(
|
||||
// Aims for interoperable compatibility with:
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27%20%3D%20Color%20Lookup-,%27nvrt%27%20%3D%20Invert,-%27post%27%20%3D%20Posterize
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
async fn invert<F: 'n + Send, T: Adjust<Color>>(
|
||||
async fn invert<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
input: impl Node<F, Output = T>,
|
||||
mut input: T,
|
||||
) -> T {
|
||||
let mut input = input.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
let color = color.to_gamma_srgb();
|
||||
|
||||
@@ -620,29 +550,19 @@ async fn invert<F: 'n + Send, T: Adjust<Color>>(
|
||||
// Aims for interoperable compatibility with:
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=post%27%20%3D%20Posterize-,%27thrs%27%20%3D%20Threshold,-%27grdm%27%20%3D%20Gradient
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
async fn threshold<F: 'n + Send, T: Adjust<Color>>(
|
||||
async fn threshold<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
image: impl Node<F, Output = T>,
|
||||
mut image: T,
|
||||
#[default(50.)] min_luminance: Percentage,
|
||||
#[default(100.)] max_luminance: Percentage,
|
||||
luminance_calc: LuminanceCalculation,
|
||||
) -> T {
|
||||
let mut input = image.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
image.adjust(|color| {
|
||||
let min_luminance = Color::srgb_to_linear(min_luminance as f32 / 100.);
|
||||
let max_luminance = Color::srgb_to_linear(max_luminance as f32 / 100.);
|
||||
|
||||
@@ -660,7 +580,7 @@ async fn threshold<F: 'n + Send, T: Adjust<Color>>(
|
||||
Color::BLACK
|
||||
}
|
||||
});
|
||||
input
|
||||
image
|
||||
}
|
||||
|
||||
trait Blend<P: Pixel> {
|
||||
@@ -723,44 +643,35 @@ impl Blend<Color> for GradientStops {
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster"))]
|
||||
async fn blend<F: 'n + Send + Copy, T: Blend<Color> + Send>(
|
||||
async fn blend<T: Blend<Color> + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
over: impl Node<F, Output = T>,
|
||||
over: T,
|
||||
#[expose]
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
under: impl Node<F, Output = T>,
|
||||
under: T,
|
||||
blend_mode: BlendMode,
|
||||
#[default(100.)] opacity: Percentage,
|
||||
) -> T {
|
||||
let over = over.eval(footprint).await;
|
||||
let under = under.eval(footprint).await;
|
||||
|
||||
over.blend(&under, |a, b| blend_colors(a, b, blend_mode, opacity / 100.))
|
||||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
fn blend_color_pair(input: (Color, Color), blend_mode: BlendMode, opacity: Percentage) -> Color {
|
||||
#[node_macro::node(category(""), skip_impl)]
|
||||
fn blend_color_pair<BlendModeNode, OpacityNode>(input: (Color, Color), blend_mode: &'n BlendModeNode, opacity: &'n OpacityNode) -> Color
|
||||
where
|
||||
BlendModeNode: Node<'n, (), Output = BlendMode> + 'n,
|
||||
OpacityNode: Node<'n, (), Output = Percentage> + 'n,
|
||||
{
|
||||
let blend_mode = blend_mode.eval(());
|
||||
let opacity = opacity.eval(());
|
||||
blend_colors(input.0, input.1, blend_mode, opacity / 100.)
|
||||
}
|
||||
|
||||
@@ -857,35 +768,24 @@ pub fn blend_colors(foreground: Color, background: Color, blend_mode: BlendMode,
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27grdm%27%20%3D%20Gradient%20Map
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Gradient%20settings%20(Photoshop%206.0)
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
async fn gradient_map<F: 'n + Send, T: Adjust<Color>>(
|
||||
async fn gradient_map<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
image: impl Node<F, Output = T>,
|
||||
mut image: T,
|
||||
gradient: GradientStops,
|
||||
reverse: bool,
|
||||
) -> T {
|
||||
let mut input = image.eval(footprint).await;
|
||||
|
||||
input.adjust(|color| {
|
||||
image.adjust(|color| {
|
||||
let intensity = color.luminance_srgb();
|
||||
let intensity = if reverse { 1. - intensity } else { intensity };
|
||||
gradient.evalute(intensity as f64)
|
||||
});
|
||||
|
||||
input
|
||||
image
|
||||
}
|
||||
|
||||
// Aims for interoperable compatibility with:
|
||||
@@ -896,27 +796,17 @@ async fn gradient_map<F: 'n + Send, T: Adjust<Color>>(
|
||||
// https://stackoverflow.com/questions/33966121/what-is-the-algorithm-for-vibrance-filters
|
||||
// The results of this implementation are very close to correct, but not quite perfect
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
async fn vibrance<F: 'n + Send, T: Adjust<Color>>(
|
||||
async fn vibrance<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
image: impl Node<F, Output = T>,
|
||||
mut image: T,
|
||||
vibrance: SignedPercentage,
|
||||
) -> T {
|
||||
let mut input = image.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
image.adjust(|color| {
|
||||
let vibrance = vibrance as f32 / 100.;
|
||||
// Slow the effect down by half when it's negative, since artifacts begin appearing past -50%.
|
||||
// So this scales the 0% to -50% range to 0% to -100%.
|
||||
@@ -963,7 +853,7 @@ async fn vibrance<F: 'n + Send, T: Adjust<Color>>(
|
||||
altered_color.map_rgb(|c| c * (1. - factor) + luminance * factor)
|
||||
}
|
||||
});
|
||||
input
|
||||
image
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
@@ -1196,23 +1086,14 @@ impl DomainWarpType {
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27mixr%27%20%3D%20Channel%20Mixer
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Lab%20color%20only-,Channel%20Mixer,-Key%20is%20%27mixr
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("channel_mixer_properties"))]
|
||||
async fn channel_mixer<F: 'n + Send, T: Adjust<Color>>(
|
||||
async fn channel_mixer<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
image: impl Node<F, Output = T>,
|
||||
mut image: T,
|
||||
|
||||
monochrome: bool,
|
||||
#[default(40.)]
|
||||
@@ -1270,8 +1151,7 @@ async fn channel_mixer<F: 'n + Send, T: Adjust<Color>>(
|
||||
// Display-only properties (not used within the node)
|
||||
_output_channel: RedGreenBlue,
|
||||
) -> T {
|
||||
let mut input = image.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
image.adjust(|color| {
|
||||
let color = color.to_gamma_srgb();
|
||||
|
||||
let (r, g, b, a) = color.components();
|
||||
@@ -1296,7 +1176,7 @@ async fn channel_mixer<F: 'n + Send, T: Adjust<Color>>(
|
||||
|
||||
color.to_linear_srgb()
|
||||
});
|
||||
input
|
||||
image
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
@@ -1356,24 +1236,15 @@ impl core::fmt::Display for SelectiveColorChoice {
|
||||
//
|
||||
// Algorithm based on:
|
||||
// https://blog.pkh.me/p/22-understanding-selective-coloring-in-adobe-photoshop.html
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
async fn selective_color<F: 'n + Send, T: Adjust<Color>>(
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("selective_color_properties"))]
|
||||
async fn selective_color<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
image: impl Node<F, Output = T>,
|
||||
mut image: T,
|
||||
mode: RelativeAbsolute,
|
||||
#[name("(Reds) Cyan")] r_c: f64,
|
||||
#[name("(Reds) Magenta")] r_m: f64,
|
||||
@@ -1413,8 +1284,7 @@ async fn selective_color<F: 'n + Send, T: Adjust<Color>>(
|
||||
#[name("(Blacks) Black")] k_k: f64,
|
||||
_colors: SelectiveColorChoice,
|
||||
) -> T {
|
||||
let mut input = image.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
image.adjust(|color| {
|
||||
let color = color.to_gamma_srgb();
|
||||
|
||||
let (r, g, b, a) = color.components();
|
||||
@@ -1488,7 +1358,7 @@ async fn selective_color<F: 'n + Send, T: Adjust<Color>>(
|
||||
|
||||
color.to_linear_srgb()
|
||||
});
|
||||
input
|
||||
image
|
||||
}
|
||||
|
||||
pub(super) trait MultiplyAlpha {
|
||||
@@ -1534,28 +1404,18 @@ where
|
||||
// https://www.axiomx.com/posterize.htm
|
||||
// This algorithm produces fully accurate output in relation to the industry standard.
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
async fn posterize<F: 'n + Send, T: Adjust<Color>>(
|
||||
async fn posterize<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
input: impl Node<F, Output = T>,
|
||||
mut input: T,
|
||||
#[default(4)]
|
||||
#[min(2.)]
|
||||
levels: u32,
|
||||
) -> T {
|
||||
let mut input = input.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
let color = color.to_gamma_srgb();
|
||||
|
||||
@@ -1577,30 +1437,20 @@ async fn posterize<F: 'n + Send, T: Adjust<Color>>(
|
||||
// Algorithm based on:
|
||||
// https://geraldbakker.nl/psnumbers/exposure.html
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("exposure_properties"))]
|
||||
async fn exposure<F: 'n + Send, T: Adjust<Color>>(
|
||||
async fn exposure<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
input: impl Node<F, Output = T>,
|
||||
mut input: T,
|
||||
exposure: f64,
|
||||
offset: f64,
|
||||
#[default(1.)]
|
||||
#[range((0.01, 10.))]
|
||||
gamma_correction: f64,
|
||||
) -> T {
|
||||
let mut input = input.eval(footprint).await;
|
||||
input.adjust(|color| {
|
||||
let adjusted = color
|
||||
// Exposure
|
||||
@@ -1619,7 +1469,7 @@ const WINDOW_SIZE: usize = 1024;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[node_macro::node(category(""))]
|
||||
fn generate_curves<C: Channel + super::Linear>(_: (), curve: Curve, #[implementations(f32, f64)] _target_format: C) -> ValueMapperNode<C> {
|
||||
fn generate_curves<C: Channel + super::Linear>(_: impl Ctx, curve: Curve, #[implementations(f32, f64)] _target_format: C) -> ValueMapperNode<C> {
|
||||
use bezier_rs::{Bezier, TValue};
|
||||
|
||||
let [mut pos, mut param]: [[f32; 2]; 2] = [[0.; 2], curve.first_handle];
|
||||
@@ -1660,31 +1510,21 @@ fn generate_curves<C: Channel + super::Linear>(_: (), curve: Curve, #[implementa
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
async fn color_overlay<F: 'n + Send, T: Adjust<Color>>(
|
||||
fn color_overlay<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
(),
|
||||
(),
|
||||
(),
|
||||
Footprint,
|
||||
Color,
|
||||
ImageFrameTable<Color>,
|
||||
GradientStops,
|
||||
)]
|
||||
footprint: F,
|
||||
#[implementations(
|
||||
() -> Color,
|
||||
() -> ImageFrameTable<Color>,
|
||||
() -> GradientStops,
|
||||
Footprint -> Color,
|
||||
Footprint -> ImageFrameTable<Color>,
|
||||
Footprint -> GradientStops,
|
||||
)]
|
||||
image: impl Node<F, Output = T>,
|
||||
mut image: T,
|
||||
#[default(Color::BLACK)] color: Color,
|
||||
blend_mode: BlendMode,
|
||||
#[default(100.)] opacity: Percentage,
|
||||
) -> T {
|
||||
let opacity = (opacity as f32 / 100.).clamp(0., 1.);
|
||||
|
||||
let mut input = image.eval(footprint).await;
|
||||
input.adjust(|pixel| {
|
||||
image.adjust(|pixel| {
|
||||
let image = pixel.map_rgb(|channel| channel * (1. - opacity));
|
||||
|
||||
// The apply blend mode function divides rgb by the alpha channel for the background. This undoes that.
|
||||
@@ -1693,7 +1533,7 @@ async fn color_overlay<F: 'n + Send, T: Adjust<Color>>(
|
||||
|
||||
Color::from_rgbaf32_unchecked(image.r() + overlay.r(), image.g() + overlay.g(), image.b() + overlay.b(), pixel.a())
|
||||
});
|
||||
input
|
||||
image
|
||||
}
|
||||
|
||||
// #[cfg(feature = "alloc")]
|
||||
@@ -1702,10 +1542,11 @@ async fn color_overlay<F: 'n + Send, T: Adjust<Color>>(
|
||||
// #[cfg(feature = "alloc")]
|
||||
// mod index_node {
|
||||
// use crate::raster::{Color, ImageFrame};
|
||||
// use crate::Ctx;
|
||||
|
||||
// #[node_macro::node(category(""))]
|
||||
// pub fn index<T: Default + Clone>(
|
||||
// _: (),
|
||||
// _: impl Ctx,
|
||||
// #[implementations(Vec<ImageFrame<Color>>, Vec<Color>)]
|
||||
// #[widget(ParsedWidgetOverride::Hidden)]
|
||||
// input: Vec<T>,
|
||||
@@ -1752,7 +1593,7 @@ mod test {
|
||||
// 100% of the output should come from the multiplied value
|
||||
let opacity = 100_f64;
|
||||
|
||||
let result = super::color_overlay((), &FutureWrapperNode(ImageFrameTable::new(image.clone())), overlay_color, BlendMode::Multiply, opacity).await;
|
||||
let result = super::color_overlay((), ImageFrameTable::new(image.clone()), overlay_color, BlendMode::Multiply, opacity);
|
||||
let result = result.one_item();
|
||||
|
||||
// The output should just be the original green and alpha channels (as we multiply them by 1 and other channels by 0)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::discrete_srgb::{float_to_srgb_u8, srgb_u8_to_float};
|
||||
use super::{Alpha, AssociatedAlpha, Luminance, LuminanceMut, Pixel, RGBMut, Rec709Primaries, RGB, SRGB};
|
||||
use super::{Alpha, AlphaMut, AssociatedAlpha, Luminance, LuminanceMut, Pixel, RGBMut, Rec709Primaries, RGB, SRGB};
|
||||
|
||||
use dyn_any::DynAny;
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -257,6 +257,11 @@ impl RGBMut for Color {
|
||||
self.blue = blue;
|
||||
}
|
||||
}
|
||||
impl AlphaMut for Color {
|
||||
fn set_alpha(&mut self, value: Self::AlphaChannel) {
|
||||
self.alpha = value;
|
||||
}
|
||||
}
|
||||
|
||||
impl Pixel for Color {
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
|
||||
Reference in New Issue
Block a user