mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-15 22:28:10 +08:00
Shaders: add BufferStruct to support bool and enums (#3109)
* node-macro: modernize `node` macro * node-macro: add `CrateIdent` struct containing resolved crate paths * shaders: add trait `BufferStruct` and derive macro * shaders: `gamma_correction` and `channel_mixer` gpu nodes * shaders: `selective_color` gpu node * shaders: `brightness_contrast_classic` gpu node * shaders: append GPU to display name * node-macro: fixup doc links * shaders: consistently append " GPU" to all shader node names
This commit is contained in:
@@ -48,6 +48,7 @@ bytemuck = { workspace = true }
|
||||
glam = { workspace = true }
|
||||
spirv-std = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
num_enum = { workspace = true }
|
||||
|
||||
# Workspace std dependencies
|
||||
specta = { workspace = true, optional = true }
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use crate::adjust::Adjust;
|
||||
use crate::cubic_spline::CubicSplines;
|
||||
use core::fmt::Debug;
|
||||
use glam::{Vec3, Vec4};
|
||||
#[cfg(feature = "std")]
|
||||
use graphene_core::gradient::GradientStops;
|
||||
#[cfg(feature = "std")]
|
||||
@@ -12,6 +13,8 @@ use graphene_core::table::Table;
|
||||
use graphene_core_shaders::color::Color;
|
||||
use graphene_core_shaders::context::Ctx;
|
||||
use graphene_core_shaders::registry::types::{AngleF32, PercentageF32, SignedPercentageF32};
|
||||
use node_macro::BufferStruct;
|
||||
use num_enum::{FromPrimitive, IntoPrimitive};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use num_traits::float::Float;
|
||||
|
||||
@@ -30,7 +33,7 @@ use num_traits::float::Float;
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27clrL%27%20%3D%20Color%20Lookup
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Color%20Lookup%20(Photoshop%20CS6
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, node_macro::ChoiceType, bytemuck::NoUninit)]
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, node_macro::ChoiceType, bytemuck::NoUninit, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Dropdown)]
|
||||
#[repr(u32)]
|
||||
@@ -70,7 +73,7 @@ fn luminance<T: Adjust<Color>>(
|
||||
input
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster"), cfg(feature = "std"))]
|
||||
#[node_macro::node(category("Raster"), shader_node(PerPixelAdjust))]
|
||||
fn gamma_correction<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
@@ -138,6 +141,38 @@ fn make_opaque<T: Adjust<Color>>(
|
||||
input
|
||||
}
|
||||
|
||||
/// See [`brightness_contrast`]
|
||||
#[node_macro::node(
|
||||
name("Brightness/Contrast classic"),
|
||||
category("Raster: Adjustment"),
|
||||
properties("brightness_contrast_properties"),
|
||||
shader_node(PerPixelAdjust)
|
||||
)]
|
||||
fn brightness_contrast_classic<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
Table<GradientStops>,
|
||||
GradientStops,
|
||||
)]
|
||||
#[gpu_image]
|
||||
mut input: T,
|
||||
brightness: SignedPercentageF32,
|
||||
contrast: SignedPercentageF32,
|
||||
) -> T {
|
||||
let brightness = brightness / 255.;
|
||||
|
||||
let contrast = contrast / 100.;
|
||||
let contrast = if contrast > 0. { (contrast * core::f32::consts::FRAC_PI_2 - 0.01).tan() } else { contrast };
|
||||
|
||||
let offset = brightness * contrast + brightness - contrast / 2.;
|
||||
|
||||
input.adjust(|color| color.to_gamma_srgb().map_rgb(|c| (c + c * contrast + offset).clamp(0., 1.)).to_linear_srgb());
|
||||
|
||||
input
|
||||
}
|
||||
|
||||
// Aims for interoperable compatibility with:
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27brit%27%20%3D%20Brightness/Contrast
|
||||
// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Padding-,Brightness%20and%20Contrast,-Key%20is%20%27brit
|
||||
@@ -146,7 +181,7 @@ fn make_opaque<T: Adjust<Color>>(
|
||||
// https://geraldbakker.nl/psnumbers/brightness-contrast.html
|
||||
#[node_macro::node(name("Brightness/Contrast"), category("Raster: Adjustment"), properties("brightness_contrast_properties"), cfg(feature = "std"))]
|
||||
fn brightness_contrast<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
_ctx: impl Ctx,
|
||||
#[implementations(
|
||||
Table<Raster<CPU>>,
|
||||
Table<Color>,
|
||||
@@ -160,16 +195,7 @@ fn brightness_contrast<T: Adjust<Color>>(
|
||||
use_classic: bool,
|
||||
) -> T {
|
||||
if use_classic {
|
||||
let brightness = brightness / 255.;
|
||||
|
||||
let contrast = contrast / 100.;
|
||||
let contrast = if contrast > 0. { (contrast * core::f32::consts::FRAC_PI_2 - 0.01).tan() } else { contrast };
|
||||
|
||||
let offset = brightness * contrast + brightness - contrast / 2.;
|
||||
|
||||
input.adjust(|color| color.to_gamma_srgb().map_rgb(|c| (c + c * contrast + offset).clamp(0., 1.)).to_linear_srgb());
|
||||
|
||||
return input;
|
||||
return brightness_contrast_classic(_ctx, input, brightness, contrast);
|
||||
}
|
||||
|
||||
const WINDOW_SIZE: usize = 1024;
|
||||
@@ -549,7 +575,8 @@ fn vibrance<T: Adjust<Color>>(
|
||||
}
|
||||
|
||||
/// Color Channel
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Radio)]
|
||||
pub enum RedGreenBlue {
|
||||
@@ -560,7 +587,7 @@ pub enum RedGreenBlue {
|
||||
}
|
||||
|
||||
/// Color Channel
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, bytemuck::NoUninit)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, bytemuck::NoUninit, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Radio)]
|
||||
#[repr(u32)]
|
||||
@@ -653,7 +680,7 @@ pub enum DomainWarpType {
|
||||
// Aims for interoperable compatibility with:
|
||||
// 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"), cfg(feature = "std"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("channel_mixer_properties"), shader_node(PerPixelAdjust))]
|
||||
fn channel_mixer<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
@@ -750,7 +777,8 @@ fn channel_mixer<T: Adjust<Color>>(
|
||||
image
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Radio)]
|
||||
pub enum RelativeAbsolute {
|
||||
@@ -759,8 +787,8 @@ pub enum RelativeAbsolute {
|
||||
Absolute,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType, BufferStruct, FromPrimitive, IntoPrimitive)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
pub enum SelectiveColorChoice {
|
||||
#[default]
|
||||
@@ -783,7 +811,7 @@ pub enum SelectiveColorChoice {
|
||||
//
|
||||
// Algorithm based on:
|
||||
// https://blog.pkh.me/p/22-understanding-selective-coloring-in-adobe-photoshop.html
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("selective_color_properties"), cfg(feature = "std"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("selective_color_properties"), shader_node(PerPixelAdjust))]
|
||||
fn selective_color<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
@@ -877,7 +905,7 @@ fn selective_color<T: Adjust<Color>>(
|
||||
RelativeAbsolute::Absolute => (-1., -1., -1.),
|
||||
};
|
||||
|
||||
let (sum_r, sum_g, sum_b) = [
|
||||
let array = [
|
||||
(SelectiveColorChoice::Reds, (r_c, r_m, r_y, r_k)),
|
||||
(SelectiveColorChoice::Yellows, (y_c, y_m, y_y, y_k)),
|
||||
(SelectiveColorChoice::Greens, (g_c, g_m, g_y, g_k)),
|
||||
@@ -887,14 +915,16 @@ fn selective_color<T: Adjust<Color>>(
|
||||
(SelectiveColorChoice::Whites, (w_c, w_m, w_y, w_k)),
|
||||
(SelectiveColorChoice::Neutrals, (n_c, n_m, n_y, n_k)),
|
||||
(SelectiveColorChoice::Blacks, (k_c, k_m, k_y, k_k)),
|
||||
]
|
||||
.into_iter()
|
||||
.fold((0., 0., 0.), |acc, (color_parameter_group, (c, m, y, k))| {
|
||||
];
|
||||
let mut sum = Vec3::ZERO;
|
||||
for i in 0..array.len() {
|
||||
let (color_parameter_group, (c, m, y, k)) = array[i];
|
||||
|
||||
// Skip this color parameter group...
|
||||
// ...if it's unchanged from the default of zero offset on all CMYK parameters, or...
|
||||
// ...if this pixel's color isn't in the range affected by this color parameter group
|
||||
if (c < f32::EPSILON && m < f32::EPSILON && y < f32::EPSILON && k < f32::EPSILON) || (!pixel_color_range(color_parameter_group)) {
|
||||
return acc;
|
||||
continue;
|
||||
}
|
||||
|
||||
let (c, m, y, k) = (c / 100., m / 100., y / 100., k / 100.);
|
||||
@@ -907,14 +937,15 @@ fn selective_color<T: Adjust<Color>>(
|
||||
SelectiveColorChoice::Blacks => 1. - max(r, g, b) * 2.,
|
||||
};
|
||||
|
||||
let offset_r = ((c + k * (c + 1.)) * slope_r).clamp(-r, -r + 1.) * color_parameter_group_scale_factor;
|
||||
let offset_g = ((m + k * (m + 1.)) * slope_g).clamp(-g, -g + 1.) * color_parameter_group_scale_factor;
|
||||
let offset_b = ((y + k * (y + 1.)) * slope_b).clamp(-b, -b + 1.) * color_parameter_group_scale_factor;
|
||||
let offset_r = f32::clamp((c + k * (c + 1.)) * slope_r, -r, -r + 1.) * color_parameter_group_scale_factor;
|
||||
let offset_g = f32::clamp((m + k * (m + 1.)) * slope_g, -g, -g + 1.) * color_parameter_group_scale_factor;
|
||||
let offset_b = f32::clamp((y + k * (y + 1.)) * slope_b, -b, -b + 1.) * color_parameter_group_scale_factor;
|
||||
|
||||
(acc.0 + offset_r, acc.1 + offset_g, acc.2 + offset_b)
|
||||
});
|
||||
sum += Vec3::new(offset_r, offset_g, offset_b);
|
||||
}
|
||||
|
||||
let color = Color::from_rgbaf32_unchecked((r + sum_r).clamp(0., 1.), (g + sum_g).clamp(0., 1.), (b + sum_b).clamp(0., 1.), a);
|
||||
let rgb = Vec3::new(r, g, b);
|
||||
let color = Color::from_vec4(Vec4::from(((sum + rgb).clamp(Vec3::ZERO, Vec3::ONE), a)));
|
||||
|
||||
color.to_linear_srgb()
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user