mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Shaders: add gcore-shaders and make graster-nodes no-std (#2925)
* gcore-shaders: add crate, move `color` and `blending` from gcore * gcore-shaders: move `AsU32` * gcore-shaders: move `ChoiceType`, switch `Cow` for `&str`, adjust node macro * gcore-shaders: move `registry::types` * gcore-shaders: move `context::Ctx` * raster-nodes: make it `no_std` with `std` feature * gcore-shaders: fix doctest
This commit is contained in:
parent
4d5a1a6ff1
commit
4fec24893e
28 changed files with 364 additions and 247 deletions
35
node-graph/gcore-shaders/Cargo.toml
Normal file
35
node-graph/gcore-shaders/Cargo.toml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
[package]
|
||||
name = "graphene-core-shaders"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
description = "no_std API definitions for Graphene"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
std = ["dep:dyn-any", "dep:serde", "dep:specta", "dep:log"]
|
||||
|
||||
[dependencies]
|
||||
# Local std dependencies
|
||||
dyn-any = { workspace = true, optional = true }
|
||||
|
||||
# Workspace dependencies
|
||||
bytemuck = { workspace = true }
|
||||
glam = { version = "0.29", default-features = false, features = ["nostd-libm", "scalar-math"] }
|
||||
half = { workspace = true }
|
||||
num-derive = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
|
||||
# Workspace std dependencies
|
||||
serde = { workspace = true, optional = true }
|
||||
specta = { workspace = true, optional = true }
|
||||
log = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
graphene-core = { workspace = true }
|
||||
|
||||
[lints.rust]
|
||||
# the spirv target is not in the list of common cfgs so must be added manually
|
||||
unexpected_cfgs = { level = "warn", check-cfg = [
|
||||
'cfg(target_arch, values("spirv"))',
|
||||
] }
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
use dyn_any::DynAny;
|
||||
use std::hash::Hash;
|
||||
use core::fmt::Display;
|
||||
use core::hash::{Hash, Hasher};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, DynAny, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "std", serde(default))]
|
||||
pub struct AlphaBlending {
|
||||
pub blend_mode: BlendMode,
|
||||
pub opacity: f32,
|
||||
|
|
@ -15,14 +16,14 @@ impl Default for AlphaBlending {
|
|||
}
|
||||
}
|
||||
impl Hash for AlphaBlending {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.opacity.to_bits().hash(state);
|
||||
self.fill.to_bits().hash(state);
|
||||
self.blend_mode.hash(state);
|
||||
self.clip.hash(state);
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for AlphaBlending {
|
||||
impl Display for AlphaBlending {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let round = |x: f32| (x * 1e3).round() / 1e3;
|
||||
write!(
|
||||
|
|
@ -62,9 +63,9 @@ impl AlphaBlending {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, DynAny, Hash, specta::Type)]
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
pub enum BlendMode {
|
||||
// Basic group
|
||||
#[default]
|
||||
|
|
@ -189,18 +190,19 @@ impl BlendMode {
|
|||
}
|
||||
|
||||
/// Renders the blend mode CSS style declaration.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn render(&self) -> String {
|
||||
format!(
|
||||
r#" mix-blend-mode: {};"#,
|
||||
self.to_svg_style_name().unwrap_or_else(|| {
|
||||
warn!("Unsupported blend mode {self:?}");
|
||||
log::warn!("Unsupported blend mode {self:?}");
|
||||
"normal"
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BlendMode {
|
||||
impl Display for BlendMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
// Normal group
|
||||
26
node-graph/gcore-shaders/src/choice_type.rs
Normal file
26
node-graph/gcore-shaders/src/choice_type.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
pub trait ChoiceTypeStatic: Sized + Copy + crate::AsU32 + Send + Sync {
|
||||
const WIDGET_HINT: ChoiceWidgetHint;
|
||||
const DESCRIPTION: Option<&'static str>;
|
||||
fn list() -> &'static [&'static [(Self, VariantMetadata)]];
|
||||
}
|
||||
|
||||
pub enum ChoiceWidgetHint {
|
||||
Dropdown,
|
||||
RadioButtons,
|
||||
}
|
||||
|
||||
/// Translation struct between macro and definition.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VariantMetadata {
|
||||
/// Name as declared in source code.
|
||||
pub name: &'static str,
|
||||
|
||||
/// Name to be displayed in UI.
|
||||
pub label: &'static str,
|
||||
|
||||
/// User-facing documentation text.
|
||||
pub docstring: Option<&'static str>,
|
||||
|
||||
/// Name of icon to display in radio buttons and such.
|
||||
pub icon: Option<&'static str>,
|
||||
}
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
use super::color_traits::{Alpha, AlphaMut, AssociatedAlpha, Luminance, LuminanceMut, Pixel, RGB, RGBMut, Rec709Primaries, SRGB};
|
||||
use super::discrete_srgb::{float_to_srgb_u8, srgb_u8_to_float};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use dyn_any::DynAny;
|
||||
use core::hash::Hash;
|
||||
use half::f16;
|
||||
#[cfg(target_arch = "spirv")]
|
||||
use spirv_std::num_traits::Euclid;
|
||||
#[cfg(target_arch = "spirv")]
|
||||
use spirv_std::num_traits::float::Float;
|
||||
use std::hash::Hash;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, Pod, Zeroable, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Pod, Zeroable)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, serde::Serialize, serde::Deserialize))]
|
||||
pub struct RGBA16F {
|
||||
red: f16,
|
||||
green: f16,
|
||||
|
|
@ -82,7 +82,8 @@ impl Alpha for RGBA16F {
|
|||
impl Pixel for RGBA16F {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, Pod, Zeroable, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Pod, Zeroable)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
pub struct SRGBA8 {
|
||||
red: u8,
|
||||
green: u8,
|
||||
|
|
@ -162,7 +163,8 @@ impl Alpha for SRGBA8 {
|
|||
impl Pixel for SRGBA8 {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, Pod, Zeroable, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Pod, Zeroable)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
pub struct Luma(pub f32);
|
||||
|
||||
impl Luminance for Luma {
|
||||
|
|
@ -202,7 +204,8 @@ impl Pixel for Luma {}
|
|||
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`,
|
||||
/// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, DynAny, Pod, Zeroable, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Pod, Zeroable)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
pub struct Color {
|
||||
red: f32,
|
||||
green: f32,
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
use glam::DVec2;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[cfg(target_arch = "spirv")]
|
||||
use spirv_std::num_traits::float::Float;
|
||||
|
||||
pub use crate::blending::*;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use core::fmt::Debug;
|
||||
use glam::DVec2;
|
||||
use num_derive::*;
|
||||
#[cfg(target_arch = "spirv")]
|
||||
use num_traits::float::Float;
|
||||
|
||||
pub trait Linear {
|
||||
fn from_f32(x: f32) -> Self;
|
||||
|
|
@ -64,7 +63,6 @@ impl<T: Linear + Debug + Copy> Channel for T {
|
|||
|
||||
impl<T: Linear + Debug + Copy> LinearChannel for T {}
|
||||
|
||||
use num_derive::*;
|
||||
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Num, NumCast, NumOps, One, Zero, ToPrimitive, FromPrimitive)]
|
||||
pub struct SRGBGammaFloat(f32);
|
||||
|
||||
|
|
@ -97,14 +95,6 @@ impl<T: Rec709Primaries> RGBPrimaries for T {
|
|||
|
||||
pub trait SRGB: Rec709Primaries {}
|
||||
|
||||
pub trait Serde: serde::Serialize + for<'a> serde::Deserialize<'a> {}
|
||||
#[cfg(not(feature = "serde"))]
|
||||
pub trait Serde {}
|
||||
|
||||
impl<T: serde::Serialize + for<'a> serde::Deserialize<'a>> Serde for T {}
|
||||
#[cfg(not(feature = "serde"))]
|
||||
impl<T> Serde for T {}
|
||||
|
||||
// TODO: Come up with a better name for this trait
|
||||
pub trait Pixel: Clone + Pod + Zeroable + Default {
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
9
node-graph/gcore-shaders/src/context.rs
Normal file
9
node-graph/gcore-shaders/src/context.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
pub trait Ctx: Clone + Send {}
|
||||
|
||||
impl<T: Ctx> Ctx for Option<T> {}
|
||||
impl<T: Ctx + Sync> Ctx for &T {}
|
||||
impl Ctx for () {}
|
||||
|
||||
pub trait ArcCtx: Send + Sync {}
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: ArcCtx> Ctx for std::sync::Arc<T> {}
|
||||
17
node-graph/gcore-shaders/src/lib.rs
Normal file
17
node-graph/gcore-shaders/src/lib.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
pub mod blending;
|
||||
pub mod choice_type;
|
||||
pub mod color;
|
||||
pub mod context;
|
||||
pub mod registry;
|
||||
|
||||
pub use context::Ctx;
|
||||
pub use glam;
|
||||
|
||||
pub trait AsU32 {
|
||||
fn as_u32(&self) -> u32;
|
||||
}
|
||||
impl AsU32 for u32 {
|
||||
fn as_u32(&self) -> u32 {
|
||||
*self
|
||||
}
|
||||
}
|
||||
24
node-graph/gcore-shaders/src/registry.rs
Normal file
24
node-graph/gcore-shaders/src/registry.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
pub mod types {
|
||||
/// 0% - 100%
|
||||
pub type Percentage = f64;
|
||||
/// -100% - 100%
|
||||
pub type SignedPercentage = f64;
|
||||
/// -180° - 180°
|
||||
pub type Angle = f64;
|
||||
/// Ends in the unit of x
|
||||
pub type Multiplier = f64;
|
||||
/// Non-negative integer with px unit
|
||||
pub type PixelLength = f64;
|
||||
/// Non-negative
|
||||
pub type Length = f64;
|
||||
/// 0 to 1
|
||||
pub type Fraction = f64;
|
||||
/// Unsigned integer
|
||||
pub type IntegerCount = u32;
|
||||
/// Unsigned integer to be used for random seeds
|
||||
pub type SeedValue = u32;
|
||||
/// DVec2 with px unit
|
||||
pub type PixelSize = glam::DVec2;
|
||||
/// String with one or more than one line
|
||||
pub type TextArea = String;
|
||||
}
|
||||
|
|
@ -14,10 +14,12 @@ wgpu = ["dep:wgpu"]
|
|||
dealloc_nodes = []
|
||||
|
||||
[dependencies]
|
||||
# Local dependencies
|
||||
graphene-core-shaders = { workspace = true, features = ["std"] }
|
||||
|
||||
# Workspace dependencies
|
||||
bytemuck = { workspace = true }
|
||||
node-macro = { workspace = true }
|
||||
num-derive = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
glam = { workspace = true }
|
||||
|
|
@ -30,7 +32,6 @@ rand_chacha = { workspace = true }
|
|||
bezier-rs = { workspace = true }
|
||||
specta = { workspace = true }
|
||||
image = { workspace = true }
|
||||
half = { workspace = true }
|
||||
tinyvec = { workspace = true }
|
||||
parley = { workspace = true }
|
||||
skrifa = { workspace = true }
|
||||
|
|
@ -46,9 +47,3 @@ wgpu = { workspace = true, optional = true }
|
|||
# Workspace dependencies
|
||||
tokio = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
[lints.rust]
|
||||
# the spirv target is not in the list of common cfgs so must be added manually
|
||||
unexpected_cfgs = { level = "warn", check-cfg = [
|
||||
'cfg(target_arch, values("spirv"))',
|
||||
] }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use crate::transform::Footprint;
|
||||
pub use graphene_core_shaders::context::{ArcCtx, Ctx};
|
||||
use std::any::Any;
|
||||
use std::borrow::Borrow;
|
||||
use std::panic::Location;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait Ctx: Clone + Send {}
|
||||
|
||||
pub trait ExtractFootprint {
|
||||
#[track_caller]
|
||||
fn try_footprint(&self) -> Option<&Footprint>;
|
||||
|
|
@ -51,9 +50,6 @@ pub enum VarArgsResult {
|
|||
IndexOutOfBounds,
|
||||
NoVarArgs,
|
||||
}
|
||||
impl<T: Ctx> Ctx for Option<T> {}
|
||||
impl<T: Ctx + Sync> Ctx for &T {}
|
||||
impl Ctx for () {}
|
||||
impl Ctx for Footprint {}
|
||||
impl ExtractFootprint for () {
|
||||
fn try_footprint(&self) -> Option<&Footprint> {
|
||||
|
|
@ -157,7 +153,7 @@ impl<T: CloneVarArgs + Sync> CloneVarArgs for Arc<T> {
|
|||
}
|
||||
|
||||
impl Ctx for ContextImpl<'_> {}
|
||||
impl Ctx for Arc<OwnedContextImpl> {}
|
||||
impl ArcCtx for OwnedContextImpl {}
|
||||
|
||||
impl ExtractFootprint for ContextImpl<'_> {
|
||||
fn try_footprint(&self) -> Option<&Footprint> {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@
|
|||
extern crate log;
|
||||
|
||||
pub mod animation;
|
||||
pub mod blending;
|
||||
pub mod blending_nodes;
|
||||
pub mod bounds;
|
||||
pub mod color;
|
||||
pub mod consts;
|
||||
pub mod context;
|
||||
pub mod debug;
|
||||
|
|
@ -33,13 +31,17 @@ pub mod vector;
|
|||
|
||||
pub use crate as graphene_core;
|
||||
pub use blending::*;
|
||||
pub use color::Color;
|
||||
pub use context::*;
|
||||
pub use ctor;
|
||||
pub use dyn_any::{StaticTypeSized, WasmNotSend, WasmNotSync};
|
||||
pub use graphene_core_shaders::AsU32;
|
||||
pub use graphene_core_shaders::blending;
|
||||
pub use graphene_core_shaders::choice_type;
|
||||
pub use graphene_core_shaders::color;
|
||||
pub use graphic_element::{Artboard, ArtboardGroupTable, GraphicElement, GraphicGroupTable};
|
||||
pub use memo::MemoHash;
|
||||
pub use num_traits;
|
||||
pub use raster::Color;
|
||||
use std::any::TypeId;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
|
@ -165,12 +167,3 @@ pub trait NodeInputDecleration {
|
|||
fn identifier() -> ProtoNodeIdentifier;
|
||||
type Result;
|
||||
}
|
||||
|
||||
pub trait AsU32 {
|
||||
fn as_u32(&self) -> u32;
|
||||
}
|
||||
impl AsU32 for u32 {
|
||||
fn as_u32(&self) -> u32 {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@ pub mod color {
|
|||
|
||||
pub mod image;
|
||||
|
||||
pub use self::image::{Image, TransformImage};
|
||||
pub use self::image::Image;
|
||||
use crate::GraphicGroupTable;
|
||||
pub use crate::color::*;
|
||||
use crate::raster_types::{CPU, RasterDataTable};
|
||||
use crate::vector::VectorDataTable;
|
||||
#[cfg(target_arch = "spirv")]
|
||||
use spirv_std::num_traits::float::Float;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Bitmap {
|
||||
|
|
|
|||
|
|
@ -1,36 +1,12 @@
|
|||
use crate::{Node, NodeIO, NodeIOTypes, ProtoNodeIdentifier, Type, WasmNotSend};
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::pin::Pin;
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
pub mod types {
|
||||
/// 0% - 100%
|
||||
pub type Percentage = f64;
|
||||
/// -100% - 100%
|
||||
pub type SignedPercentage = f64;
|
||||
/// -180° - 180°
|
||||
pub type Angle = f64;
|
||||
/// Ends in the unit of x
|
||||
pub type Multiplier = f64;
|
||||
/// Non-negative integer with px unit
|
||||
pub type PixelLength = f64;
|
||||
/// Non-negative
|
||||
pub type Length = f64;
|
||||
/// 0 to 1
|
||||
pub type Fraction = f64;
|
||||
/// Unsigned integer
|
||||
pub type IntegerCount = u32;
|
||||
/// Unsigned integer to be used for random seeds
|
||||
pub type SeedValue = u32;
|
||||
/// DVec2 with px unit
|
||||
pub type PixelSize = glam::DVec2;
|
||||
/// String with one or more than one line
|
||||
pub type TextArea = String;
|
||||
}
|
||||
pub use graphene_core_shaders::registry::types;
|
||||
|
||||
// Translation struct between macro and definition
|
||||
#[derive(Clone)]
|
||||
|
|
@ -59,33 +35,6 @@ pub struct FieldMetadata {
|
|||
pub unit: Option<&'static str>,
|
||||
}
|
||||
|
||||
pub trait ChoiceTypeStatic: Sized + Copy + crate::AsU32 + Send + Sync {
|
||||
const WIDGET_HINT: ChoiceWidgetHint;
|
||||
const DESCRIPTION: Option<&'static str>;
|
||||
fn list() -> &'static [&'static [(Self, VariantMetadata)]];
|
||||
}
|
||||
|
||||
pub enum ChoiceWidgetHint {
|
||||
Dropdown,
|
||||
RadioButtons,
|
||||
}
|
||||
|
||||
/// Translation struct between macro and definition.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VariantMetadata {
|
||||
/// Name as declared in source code.
|
||||
pub name: Cow<'static, str>,
|
||||
|
||||
/// Name to be displayed in UI.
|
||||
pub label: Cow<'static, str>,
|
||||
|
||||
/// User-facing documentation text.
|
||||
pub docstring: Option<Cow<'static, str>>,
|
||||
|
||||
/// Name of icon to display in radio buttons and such.
|
||||
pub icon: Option<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum RegistryWidgetOverride {
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -120,7 +120,6 @@ impl<'i, T: Clone + 'i> Node<'i, ()> for DebugClonedNode<T> {
|
|||
type Output = T;
|
||||
#[inline(always)]
|
||||
fn eval(&'i self, _input: ()) -> Self::Output {
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
// KEEP THIS `debug!()` - It acts as the output for the debug node itself
|
||||
log::debug!("DebugClonedNode::eval");
|
||||
|
||||
|
|
|
|||
|
|
@ -7,28 +7,44 @@ authors = ["Graphite Authors <contact@graphite.rs>"]
|
|||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[features]
|
||||
default = ["serde"]
|
||||
serde = ["dep:serde"]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"dep:graphene-core",
|
||||
"dep:dyn-any",
|
||||
"dep:image",
|
||||
"dep:ndarray",
|
||||
"dep:bezier-rs",
|
||||
"dep:rand",
|
||||
"dep:rand_chacha",
|
||||
"dep:fastnoise-lite",
|
||||
"dep:serde",
|
||||
"dep:specta",
|
||||
"dep:glam"
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
# Local dependencies
|
||||
dyn-any = { workspace = true }
|
||||
graphene-core = { workspace = true }
|
||||
graphene-core-shaders = { workspace = true }
|
||||
node-macro = { workspace = true }
|
||||
|
||||
# Workspace dependencies
|
||||
glam = { workspace = true }
|
||||
specta = { workspace = true }
|
||||
image = { workspace = true }
|
||||
bytemuck = { workspace = true }
|
||||
ndarray = { workspace = true }
|
||||
bezier-rs = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
rand_chacha = { workspace = true }
|
||||
fastnoise-lite = { workspace = true }
|
||||
# Local std dependencies
|
||||
dyn-any = { workspace = true, optional = true }
|
||||
graphene-core = { workspace = true, optional = true }
|
||||
|
||||
# Optional workspace dependencies
|
||||
serde = { workspace = true, optional = true, features = ["derive"] }
|
||||
# Workspace dependencies
|
||||
bytemuck = { workspace = true }
|
||||
# glam is reexported from gcore-shaders in no_std mode
|
||||
glam = { workspace = true, optional = true }
|
||||
|
||||
# Workspace std dependencies
|
||||
specta = { workspace = true, optional = true }
|
||||
image = { workspace = true, optional = true }
|
||||
ndarray = { workspace = true, optional = true }
|
||||
bezier-rs = { workspace = true, optional = true }
|
||||
rand = { workspace = true, optional = true }
|
||||
rand_chacha = { workspace = true, optional = true }
|
||||
fastnoise-lite = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use graphene_core::Color;
|
||||
use graphene_core::gradient::GradientStops;
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_core_shaders::color::Color;
|
||||
|
||||
pub trait Adjust<P> {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&P) -> P);
|
||||
|
|
@ -17,19 +15,26 @@ impl Adjust<Color> for Option<Color> {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl Adjust<Color> for GradientStops {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
for (_pos, c) in self.iter_mut() {
|
||||
*c = map_fn(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Adjust<Color> for RasterDataTable<CPU> {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
for c in instance.instance.data_mut().data.iter_mut() {
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod adjust_std {
|
||||
use super::*;
|
||||
use graphene_core::gradient::GradientStops;
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
impl Adjust<Color> for GradientStops {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
for (_pos, c) in self.iter_mut() {
|
||||
*c = map_fn(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Adjust<Color> for RasterDataTable<CPU> {
|
||||
fn adjust(&mut self, map_fn: impl Fn(&Color) -> Color) {
|
||||
for instance in self.instance_mut_iter() {
|
||||
for c in instance.instance.data_mut().data.iter_mut() {
|
||||
*c = map_fn(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
use crate::adjust::Adjust;
|
||||
use crate::cubic_spline::CubicSplines;
|
||||
use dyn_any::DynAny;
|
||||
use graphene_core::color::Color;
|
||||
use graphene_core::context::Ctx;
|
||||
use core::fmt::Debug;
|
||||
#[cfg(feature = "std")]
|
||||
use graphene_core::gradient::GradientStops;
|
||||
#[cfg(feature = "std")]
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_core::registry::types::{Angle, Percentage, SignedPercentage};
|
||||
use std::fmt::Debug;
|
||||
use graphene_core_shaders::color::Color;
|
||||
use graphene_core_shaders::context::Ctx;
|
||||
use graphene_core_shaders::registry::types::{Angle, Percentage, SignedPercentage};
|
||||
|
||||
// TODO: Implement the following:
|
||||
// Color Balance
|
||||
|
|
@ -25,7 +26,8 @@ use std::fmt::Debug;
|
|||
// 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, DynAny, Hash, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Dropdown)]
|
||||
pub enum LuminanceCalculation {
|
||||
#[default]
|
||||
|
|
@ -37,7 +39,7 @@ pub enum LuminanceCalculation {
|
|||
MaximumChannels,
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), shader_node(PerPixelAdjust))]
|
||||
fn luminance<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -61,7 +63,7 @@ fn luminance<T: Adjust<Color>>(
|
|||
input
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster"))]
|
||||
#[node_macro::node(category("Raster"), shader_node(PerPixelAdjust))]
|
||||
fn gamma_correction<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -81,7 +83,7 @@ fn gamma_correction<T: Adjust<Color>>(
|
|||
input
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster: Channels"))]
|
||||
#[node_macro::node(category("Raster: Channels"), shader_node(PerPixelAdjust))]
|
||||
fn extract_channel<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -104,7 +106,7 @@ fn extract_channel<T: Adjust<Color>>(
|
|||
input
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster: Channels"))]
|
||||
#[node_macro::node(category("Raster: Channels"), shader_node(PerPixelAdjust))]
|
||||
fn make_opaque<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -129,7 +131,7 @@ fn make_opaque<T: Adjust<Color>>(
|
|||
//
|
||||
// Some further analysis available at:
|
||||
// https://geraldbakker.nl/psnumbers/brightness-contrast.html
|
||||
#[node_macro::node(name("Brightness/Contrast"), category("Raster: Adjustment"), properties("brightness_contrast_properties"))]
|
||||
#[node_macro::node(name("Brightness/Contrast"), category("Raster: Adjustment"), properties("brightness_contrast_properties"), shader_node(PerPixelAdjust))]
|
||||
fn brightness_contrast<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -146,7 +148,7 @@ fn brightness_contrast<T: Adjust<Color>>(
|
|||
let brightness = brightness as f32 / 255.;
|
||||
|
||||
let contrast = contrast as f32 / 100.;
|
||||
let contrast = if contrast > 0. { (contrast * std::f32::consts::FRAC_PI_2 - 0.01).tan() } else { contrast };
|
||||
let contrast = if contrast > 0. { (contrast * core::f32::consts::FRAC_PI_2 - 0.01).tan() } else { contrast };
|
||||
|
||||
let offset = brightness * contrast + brightness - contrast / 2.;
|
||||
|
||||
|
|
@ -168,13 +170,13 @@ fn brightness_contrast<T: Adjust<Color>>(
|
|||
y: [0., 130. + brightness * 51., 233. + brightness * 10., 255.].map(|x| x / 255.),
|
||||
};
|
||||
let brightness_curve_solutions = brightness_curve_points.solve();
|
||||
let mut brightness_lut: [f32; WINDOW_SIZE] = std::array::from_fn(|i| {
|
||||
let mut brightness_lut: [f32; WINDOW_SIZE] = core::array::from_fn(|i| {
|
||||
let x = i as f32 / (WINDOW_SIZE as f32 - 1.);
|
||||
brightness_curve_points.interpolate(x, &brightness_curve_solutions)
|
||||
});
|
||||
// Special handling for when brightness is negative
|
||||
if brightness_is_negative {
|
||||
brightness_lut = std::array::from_fn(|i| {
|
||||
brightness_lut = core::array::from_fn(|i| {
|
||||
let mut x = i;
|
||||
while x > 1 && brightness_lut[x] > i as f32 / WINDOW_SIZE as f32 {
|
||||
x -= 1;
|
||||
|
|
@ -193,7 +195,7 @@ fn brightness_contrast<T: Adjust<Color>>(
|
|||
y: [0., 64. - contrast * 30., 192. + contrast * 30., 255.].map(|x| x / 255.),
|
||||
};
|
||||
let contrast_curve_solutions = contrast_curve_points.solve();
|
||||
let contrast_lut: [f32; WINDOW_SIZE] = std::array::from_fn(|i| {
|
||||
let contrast_lut: [f32; WINDOW_SIZE] = core::array::from_fn(|i| {
|
||||
let x = i as f32 / (WINDOW_SIZE as f32 - 1.);
|
||||
contrast_curve_points.interpolate(x, &contrast_curve_solutions)
|
||||
});
|
||||
|
|
@ -218,7 +220,7 @@ fn brightness_contrast<T: Adjust<Color>>(
|
|||
//
|
||||
// Some further analysis available at:
|
||||
// https://geraldbakker.nl/psnumbers/levels.html
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), shader_node(PerPixelAdjust))]
|
||||
fn levels<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -285,7 +287,7 @@ fn levels<T: Adjust<Color>>(
|
|||
// Algorithm from:
|
||||
// https://stackoverflow.com/a/55233732/775283
|
||||
// Works the same for gamma and linear color
|
||||
#[node_macro::node(name("Black & White"), category("Raster: Adjustment"))]
|
||||
#[node_macro::node(name("Black & White"), category("Raster: Adjustment"), shader_node(PerPixelAdjust))]
|
||||
async fn black_and_white<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -357,7 +359,7 @@ async fn black_and_white<T: Adjust<Color>>(
|
|||
// 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"))]
|
||||
#[node_macro::node(name("Hue/Saturation"), category("Raster: Adjustment"), shader_node(PerPixelAdjust))]
|
||||
async fn hue_saturation<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -391,7 +393,7 @@ async fn hue_saturation<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"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), shader_node(PerPixelAdjust))]
|
||||
async fn invert<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -413,7 +415,7 @@ async fn invert<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"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), shader_node(PerPixelAdjust))]
|
||||
async fn threshold<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -458,7 +460,7 @@ async fn threshold<T: Adjust<Color>>(
|
|||
// It's not the same as the saturation component of Hue/Saturation/Value. Vibrance and Saturation are both separable.
|
||||
// When both parameters are set, it is equivalent to running this adjustment twice, with only vibrance set and then only saturation set.
|
||||
// (Except for some noise probably due to rounding error.)
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), shader_node(PerPixelAdjust))]
|
||||
async fn vibrance<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -520,7 +522,8 @@ async fn vibrance<T: Adjust<Color>>(
|
|||
}
|
||||
|
||||
/// Color Channel
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Radio)]
|
||||
pub enum RedGreenBlue {
|
||||
#[default]
|
||||
|
|
@ -530,7 +533,8 @@ pub enum RedGreenBlue {
|
|||
}
|
||||
|
||||
/// Color Channel
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Radio)]
|
||||
pub enum RedGreenBlueAlpha {
|
||||
#[default]
|
||||
|
|
@ -541,7 +545,8 @@ pub enum RedGreenBlueAlpha {
|
|||
}
|
||||
|
||||
/// Style of noise pattern
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Dropdown)]
|
||||
pub enum NoiseType {
|
||||
#[default]
|
||||
|
|
@ -556,7 +561,8 @@ pub enum NoiseType {
|
|||
WhiteNoise,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
/// Style of layered levels of the noise pattern
|
||||
pub enum FractalType {
|
||||
#[default]
|
||||
|
|
@ -572,7 +578,8 @@ pub enum FractalType {
|
|||
}
|
||||
|
||||
/// Distance function used by the cellular noise
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
pub enum CellularDistanceFunction {
|
||||
#[default]
|
||||
Euclidean,
|
||||
|
|
@ -582,7 +589,8 @@ pub enum CellularDistanceFunction {
|
|||
Hybrid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
pub enum CellularReturnType {
|
||||
CellValue,
|
||||
#[default]
|
||||
|
|
@ -601,7 +609,8 @@ pub enum CellularReturnType {
|
|||
}
|
||||
|
||||
/// Type of domain warp
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Dropdown)]
|
||||
pub enum DomainWarpType {
|
||||
#[default]
|
||||
|
|
@ -616,7 +625,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"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("channel_mixer_properties"), shader_node(PerPixelAdjust))]
|
||||
async fn channel_mixer<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -711,7 +720,8 @@ async fn channel_mixer<T: Adjust<Color>>(
|
|||
image
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
#[widget(Radio)]
|
||||
pub enum RelativeAbsolute {
|
||||
#[default]
|
||||
|
|
@ -720,7 +730,8 @@ pub enum RelativeAbsolute {
|
|||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, DynAny, node_macro::ChoiceType, specta::Type, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, node_macro::ChoiceType)]
|
||||
#[cfg_attr(feature = "std", derive(dyn_any::DynAny, specta::Type, serde::Serialize, serde::Deserialize))]
|
||||
pub enum SelectiveColorChoice {
|
||||
#[default]
|
||||
Reds,
|
||||
|
|
@ -742,7 +753,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"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("selective_color_properties"), shader_node(PerPixelAdjust))]
|
||||
async fn selective_color<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -884,7 +895,7 @@ async fn selective_color<T: Adjust<Color>>(
|
|||
// Algorithm based on:
|
||||
// https://www.axiomx.com/posterize.htm
|
||||
// This algorithm produces fully accurate output in relation to the industry standard.
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), shader_node(PerPixelAdjust))]
|
||||
async fn posterize<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -917,7 +928,7 @@ async fn posterize<T: Adjust<Color>>(
|
|||
//
|
||||
// Algorithm based on:
|
||||
// https://geraldbakker.nl/psnumbers/exposure.html
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("exposure_properties"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), properties("exposure_properties"), shader_node(PerPixelAdjust))]
|
||||
async fn exposure<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use crate::adjust::Adjust;
|
||||
use graphene_core::color::Pixel;
|
||||
#[cfg(feature = "std")]
|
||||
use graphene_core::gradient::GradientStops;
|
||||
use graphene_core::raster::Image;
|
||||
use graphene_core::raster_types::{CPU, Raster, RasterDataTable};
|
||||
use graphene_core::registry::types::Percentage;
|
||||
use graphene_core::{BlendMode, Color, Ctx};
|
||||
use std::cmp::Ordering;
|
||||
#[cfg(feature = "std")]
|
||||
use graphene_core::raster_types::{CPU, RasterDataTable};
|
||||
use graphene_core_shaders::Ctx;
|
||||
use graphene_core_shaders::blending::BlendMode;
|
||||
use graphene_core_shaders::color::{Color, Pixel};
|
||||
use graphene_core_shaders::registry::types::Percentage;
|
||||
|
||||
pub trait Blend<P: Pixel> {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(P, P) -> P) -> Self;
|
||||
|
|
@ -24,41 +25,45 @@ impl Blend<Color> for Option<Color> {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl Blend<Color> for RasterDataTable<CPU> {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut result_table = self.clone();
|
||||
|
||||
for (over, under) in result_table.instance_mut_iter().zip(under.instance_ref_iter()) {
|
||||
let data = over.instance.data.iter().zip(under.instance.data.iter()).map(|(a, b)| blend_fn(*a, *b)).collect();
|
||||
#[cfg(feature = "std")]
|
||||
mod blend_std {
|
||||
use super::*;
|
||||
use core::cmp::Ordering;
|
||||
use graphene_core::raster::Image;
|
||||
use graphene_core::raster_types::Raster;
|
||||
impl Blend<Color> for RasterDataTable<CPU> {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut result_table = self.clone();
|
||||
for (over, under) in result_table.instance_mut_iter().zip(under.instance_ref_iter()) {
|
||||
let data = over.instance.data.iter().zip(under.instance.data.iter()).map(|(a, b)| blend_fn(*a, *b)).collect();
|
||||
|
||||
*over.instance = Raster::new_cpu(Image {
|
||||
data,
|
||||
width: over.instance.width,
|
||||
height: over.instance.height,
|
||||
base64_string: None,
|
||||
});
|
||||
*over.instance = Raster::new_cpu(Image {
|
||||
data,
|
||||
width: over.instance.width,
|
||||
height: over.instance.height,
|
||||
base64_string: None,
|
||||
});
|
||||
}
|
||||
result_table
|
||||
}
|
||||
|
||||
result_table
|
||||
}
|
||||
}
|
||||
impl Blend<Color> for GradientStops {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut combined_stops = self.iter().map(|(position, _)| position).chain(under.iter().map(|(position, _)| position)).collect::<Vec<_>>();
|
||||
combined_stops.dedup_by(|&mut a, &mut b| (a - b).abs() < 1e-6);
|
||||
combined_stops.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
|
||||
|
||||
let stops = combined_stops
|
||||
.into_iter()
|
||||
.map(|&position| {
|
||||
let over_color = self.evaluate(position);
|
||||
let under_color = under.evaluate(position);
|
||||
let color = blend_fn(over_color, under_color);
|
||||
(position, color)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
GradientStops::new(stops)
|
||||
impl Blend<Color> for GradientStops {
|
||||
fn blend(&self, under: &Self, blend_fn: impl Fn(Color, Color) -> Color) -> Self {
|
||||
let mut combined_stops = self.iter().map(|(position, _)| position).chain(under.iter().map(|(position, _)| position)).collect::<Vec<_>>();
|
||||
combined_stops.dedup_by(|&mut a, &mut b| (a - b).abs() < 1e-6);
|
||||
combined_stops.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
|
||||
let stops = combined_stops
|
||||
.into_iter()
|
||||
.map(|&position| {
|
||||
let over_color = self.evaluate(position);
|
||||
let under_color = under.evaluate(position);
|
||||
let color = blend_fn(over_color, under_color);
|
||||
(position, color)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
GradientStops::new(stops)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +119,7 @@ pub fn apply_blend_mode(foreground: Color, background: Color, blend_mode: BlendM
|
|||
}
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster"))]
|
||||
#[node_macro::node(category("Raster"), shader_node(PerPixelAdjust))]
|
||||
async fn blend<T: Blend<Color> + Send>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -136,7 +141,7 @@ async fn blend<T: Blend<Color> + Send>(
|
|||
over.blend(&under, |a, b| blend_colors(a, b, blend_mode, opacity / 100.))
|
||||
}
|
||||
|
||||
#[node_macro::node(category("Raster: Adjustment"))]
|
||||
#[node_macro::node(category("Raster: Adjustment"), shader_node(PerPixelAdjust))]
|
||||
fn color_overlay<T: Adjust<Color>>(
|
||||
_: impl Ctx,
|
||||
#[implementations(
|
||||
|
|
@ -163,6 +168,7 @@ fn color_overlay<T: Adjust<Color>>(
|
|||
image
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[node_macro::node(category(""), skip_impl)]
|
||||
fn blend_color_pair<BlendModeNode, OpacityNode>(input: (Color, Color), blend_mode: &'n BlendModeNode, opacity: &'n OpacityNode) -> Color
|
||||
where
|
||||
|
|
@ -174,7 +180,7 @@ where
|
|||
blend_colors(input.0, input.1, blend_mode, opacity / 100.)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(feature = "std", test))]
|
||||
mod test {
|
||||
use graphene_core::blending::BlendMode;
|
||||
use graphene_core::color::Color;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,12 @@ impl CubicSplines {
|
|||
// Gaussian elimination: forward elimination
|
||||
for row in 0..4 {
|
||||
let pivot_row_index = (row..4)
|
||||
.max_by(|&a_row, &b_row| augmented_matrix[a_row][row].abs().partial_cmp(&augmented_matrix[b_row][row].abs()).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.max_by(|&a_row, &b_row| {
|
||||
augmented_matrix[a_row][row]
|
||||
.abs()
|
||||
.partial_cmp(&augmented_matrix[b_row][row].abs())
|
||||
.unwrap_or(core::cmp::Ordering::Equal)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// Swap the current row with the row that has the largest pivot element
|
||||
|
|
|
|||
|
|
@ -1,12 +1,24 @@
|
|||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub use graphene_core_shaders::glam;
|
||||
|
||||
pub mod adjust;
|
||||
pub mod adjustments;
|
||||
pub mod blending_nodes;
|
||||
pub mod cubic_spline;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub mod curve;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod dehaze;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod filter;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod generate_curves;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod gradient_map;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod image_color_palette;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod std_nodes;
|
||||
|
|
|
|||
|
|
@ -111,13 +111,21 @@ fn derive_enum(enum_attributes: &[Attribute], name: Ident, input: syn::DataEnum)
|
|||
})
|
||||
.collect();
|
||||
|
||||
let crate_name = proc_macro_crate::crate_name("graphene-core")
|
||||
.map_err(|e| syn::Error::new(Span::call_site(), format!("Failed to find location of graphene_core. Make sure it is imported as a dependency: {}", e)))?;
|
||||
let crate_name = match crate_name {
|
||||
proc_macro_crate::FoundCrate::Itself => quote!(crate),
|
||||
proc_macro_crate::FoundCrate::Name(name) => {
|
||||
let identifier = Ident::new(&name, Span::call_site());
|
||||
quote! { #identifier }
|
||||
let crate_name = {
|
||||
let crate_name = proc_macro_crate::crate_name("graphene-core-shaders")
|
||||
.or_else(|_e| proc_macro_crate::crate_name("graphene-core"))
|
||||
.map_err(|e| {
|
||||
syn::Error::new(
|
||||
Span::call_site(),
|
||||
format!("Failed to find location of 'graphene_core' or 'graphene-core-shaders'. Make sure it is imported as a dependency: {}", e),
|
||||
)
|
||||
})?;
|
||||
match crate_name {
|
||||
proc_macro_crate::FoundCrate::Itself => quote!(crate),
|
||||
proc_macro_crate::FoundCrate::Name(name) => {
|
||||
let identifier = Ident::new(&name, Span::call_site());
|
||||
quote! { #identifier }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -140,19 +148,19 @@ fn derive_enum(enum_attributes: &[Attribute], name: Ident, input: syn::DataEnum)
|
|||
let docstring = match &variant.basic_item.description {
|
||||
Some(s) => {
|
||||
let s = s.trim();
|
||||
quote! { Some(::std::borrow::Cow::Borrowed(#s)) }
|
||||
quote! { Some(#s) }
|
||||
}
|
||||
None => quote! { None },
|
||||
};
|
||||
let icon = match &variant.basic_item.icon {
|
||||
Some(s) => quote! { Some(::std::borrow::Cow::Borrowed(#s)) },
|
||||
Some(s) => quote! { Some(#s) },
|
||||
None => quote! { None },
|
||||
};
|
||||
quote! {
|
||||
(
|
||||
#name::#vname, #crate_name::registry::VariantMetadata {
|
||||
name: ::std::borrow::Cow::Borrowed(#vname_str),
|
||||
label: ::std::borrow::Cow::Borrowed(#label),
|
||||
#name::#vname, #crate_name::choice_type::VariantMetadata {
|
||||
name: #vname_str,
|
||||
label: #label,
|
||||
docstring: #docstring,
|
||||
icon: #icon,
|
||||
}
|
||||
|
|
@ -174,10 +182,10 @@ fn derive_enum(enum_attributes: &[Attribute], name: Ident, input: syn::DataEnum)
|
|||
}
|
||||
}
|
||||
|
||||
impl #crate_name::registry::ChoiceTypeStatic for #name {
|
||||
const WIDGET_HINT: #crate_name::registry::ChoiceWidgetHint = #crate_name::registry::ChoiceWidgetHint::#widget_hint;
|
||||
impl #crate_name::choice_type::ChoiceTypeStatic for #name {
|
||||
const WIDGET_HINT: #crate_name::choice_type::ChoiceWidgetHint = #crate_name::choice_type::ChoiceWidgetHint::#widget_hint;
|
||||
const DESCRIPTION: Option<&'static str> = #enum_description;
|
||||
fn list() -> &'static [&'static [(Self, #crate_name::registry::VariantMetadata)]] {
|
||||
fn list() -> &'static [&'static [(Self, #crate_name::choice_type::VariantMetadata)]] {
|
||||
&[ #(#group)* ]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue