diff --git a/node-graph/gcore/src/color/color.rs b/node-graph/gcore/src/color/color.rs index 69edc39e8..0ca55220d 100644 --- a/node-graph/gcore/src/color/color.rs +++ b/node-graph/gcore/src/color/color.rs @@ -835,6 +835,20 @@ impl Color { [(gamma.red * 255.) as u8, (gamma.green * 255.) as u8, (gamma.blue * 255.) as u8, (gamma.alpha * 255.) as u8] } + /// Return the all RGB components as a u8 slice, first component is red, followed by green, followed by blue. Use this if the [`Color`] is in linear space. + /// + /// # Examples + /// ``` + /// use graphene_core::color::Color; + /// let color = Color::from_rgbaf32(0.114, 0.103, 0.98, 0.97).unwrap(); + /// // TODO: Add test + /// ``` + #[inline(always)] + pub fn to_rgb8_srgb(&self) -> [u8; 3] { + let gamma = self.to_gamma_srgb(); + [(gamma.red * 255.) as u8, (gamma.green * 255.) as u8, (gamma.blue * 255.) as u8] + } + // https://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/ /// Convert a [Color] to a hue, saturation, lightness and alpha (all between 0 and 1) /// diff --git a/node-graph/gcore/src/logic.rs b/node-graph/gcore/src/logic.rs index 9be5e7c60..1c48970b7 100644 --- a/node-graph/gcore/src/logic.rs +++ b/node-graph/gcore/src/logic.rs @@ -10,10 +10,18 @@ use crate::{Context, Ctx}; use glam::{DAffine2, DVec2}; #[node_macro::node(category("Text"))] -fn to_string(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, VectorDataTable, DAffine2)] value: T) -> String { +fn to_string(_: impl Ctx, #[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, VectorDataTable)] value: T) -> String { format!("{:?}", value) } +#[node_macro::node(category("Text"))] +fn serialize( + _: impl Ctx, + #[implementations(String, bool, f64, u32, u64, DVec2, DAffine2, Color, Option, GraphicGroupTable, VectorDataTable, RasterDataTable)] value: T, +) -> String { + serde_json::to_string(&value).unwrap_or_else(|_| "Serialization Error".to_string()) +} + #[node_macro::node(category("Text"))] fn string_concatenate(_: impl Ctx, #[implementations(String)] first: String, second: TextArea) -> String { first.clone() + &second diff --git a/node-graph/graster-nodes/src/adjustments.rs b/node-graph/graster-nodes/src/adjustments.rs index 297fd1b39..eb516a723 100644 --- a/node-graph/graster-nodes/src/adjustments.rs +++ b/node-graph/graster-nodes/src/adjustments.rs @@ -65,6 +65,26 @@ fn luminance>( input } +#[node_macro::node(category("Raster"))] +fn gamma_correction>( + _: impl Ctx, + #[implementations( + Color, + RasterDataTable, + GradientStops, + )] + mut input: T, + #[default(2.2)] + #[range((0.01, 10.))] + #[hard_min(0.0001)] + gamma: f64, + inverse: bool, +) -> T { + let exponent = if inverse { 1. / gamma } else { gamma }; + input.adjust(|color| color.gamma(exponent as f32)); + input +} + #[node_macro::node(category("Raster: Channels"))] fn extract_channel>( _: impl Ctx, diff --git a/node-graph/gstd/src/http.rs b/node-graph/gstd/src/http.rs deleted file mode 100644 index bd3e77ab3..000000000 --- a/node-graph/gstd/src/http.rs +++ /dev/null @@ -1,11 +0,0 @@ -use graphene_core::Ctx; - -#[node_macro::node(category("Web Request"))] -async fn get_request(_: impl Ctx, url: String) -> reqwest::Response { - reqwest::get(url).await.unwrap() -} - -#[node_macro::node(category("Web Request"))] -async fn post_request(_: impl Ctx, url: String, body: String) -> reqwest::Response { - reqwest::Client::new().post(url).body(body).send().await.unwrap() -} diff --git a/node-graph/gstd/src/lib.rs b/node-graph/gstd/src/lib.rs index d121234c3..d21d4c9c1 100644 --- a/node-graph/gstd/src/lib.rs +++ b/node-graph/gstd/src/lib.rs @@ -1,5 +1,4 @@ pub mod any; -pub mod http; pub mod text; #[cfg(feature = "wasm")] pub mod wasm_application_io; diff --git a/node-graph/gstd/src/wasm_application_io.rs b/node-graph/gstd/src/wasm_application_io.rs index ae03edd42..92c2f2f6f 100644 --- a/node-graph/gstd/src/wasm_application_io.rs +++ b/node-graph/gstd/src/wasm_application_io.rs @@ -59,6 +59,81 @@ async fn create_surface<'a: 'n>(_: impl Ctx, editor: &'a WasmEditorApi) -> Arc String { + #[cfg(target_arch = "wasm32")] + { + if discard_result { + wasm_bindgen_futures::spawn_local(async move { + let _ = reqwest::get(url).await; + }); + return String::new(); + } + } + #[cfg(not(target_arch = "wasm32"))] + { + #[cfg(feature = "tokio")] + if discard_result { + tokio::spawn(async move { + let _ = reqwest::get(url).await; + }); + return String::new(); + } + #[cfg(not(feature = "tokio"))] + if discard_result { + return String::new(); + } + } + + let Ok(response) = reqwest::get(url).await else { return String::new() }; + response.text().await.ok().unwrap_or_default() +} + +#[node_macro::node(category("Web Request"))] +async fn post_request(_: impl Ctx, _primary: (), #[name("URL")] url: String, body: Vec, discard_result: bool) -> String { + #[cfg(target_arch = "wasm32")] + { + if discard_result { + wasm_bindgen_futures::spawn_local(async move { + let _ = reqwest::Client::new().post(url).body(body).header("Content-Type", "application/octet-stream").send().await; + }); + return String::new(); + } + } + #[cfg(not(target_arch = "wasm32"))] + { + #[cfg(feature = "tokio")] + if discard_result { + let url = url.clone(); + let body = body.clone(); + tokio::spawn(async move { + let _ = reqwest::Client::new().post(url).body(body).header("Content-Type", "application/octet-stream").send().await; + }); + return String::new(); + } + #[cfg(not(feature = "tokio"))] + if discard_result { + return String::new(); + } + } + + let Ok(response) = reqwest::Client::new().post(url).body(body).header("Content-Type", "application/octet-stream").send().await else { + return String::new(); + }; + response.text().await.ok().unwrap_or_default() +} + +#[node_macro::node(category("Web Request"), name("String to Bytes"))] +fn string_to_bytes(_: impl Ctx, string: String) -> Vec { + string.into_bytes() +} + +#[node_macro::node(category("Web Request"), name("Image to Bytes"))] +fn image_to_bytes(_: impl Ctx, image: RasterDataTable) -> Vec { + let Some(image) = image.instance_ref_iter().next() else { return vec![] }; + image.instance.data.iter().flat_map(|color| color.to_rgb8_srgb().into_iter()).collect::>() +} + #[node_macro::node(category("Web Request"))] async fn load_resource<'a: 'n>(_: impl Ctx, _primary: (), #[scope("editor-api")] editor: &'a WasmEditorApi, #[name("URL")] url: String) -> Arc<[u8]> { let Some(api) = editor.application_io.as_ref() else {