Color system based on traits, and conversion to linear color in the graph (#1123)

* Migrate Nodes to use RasterMut + Samplable

* Add Pixel trait to include serialization

* Implement traits for Color and propagate new generics

* Always convert to linear color when loading images
This commit is contained in:
Dennis Kobert
2023-04-16 02:57:05 +02:00
committed by GitHub
parent e4d4f4ad68
commit 59db45bb36
16 changed files with 638 additions and 265 deletions

View File

@@ -12,6 +12,7 @@ use document_legacy::LayerId;
use document_legacy::Operation as DocumentOperation;
use graph_craft::document::NodeId;
use graphene_core::raster::Image;
use graphene_core::Color;
use serde::{Deserialize, Serialize};
#[remain::sorted]
@@ -122,7 +123,7 @@ pub enum DocumentMessage {
resize_opposite_corner: Key,
},
PasteImage {
image: Image,
image: Image<Color>,
mouse: Option<(f64, f64)>,
},
Redo,

View File

@@ -34,7 +34,7 @@ use document_legacy::layers::text_layer::Font;
use document_legacy::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation};
use graph_craft::document::NodeId;
use graph_craft::{concrete, Type, TypeDescriptor};
use graphene_core::raster::{Color, ImageFrame};
use graphene_core::raster::{Color, ImageFrame, RasterMut};
use graphene_core::Cow;
use glam::{DAffine2, DVec2};
@@ -1057,7 +1057,7 @@ impl DocumentMessageHandler {
let primary_input_type = node_network.input_types().next().clone();
let response = match primary_input_type {
// Only calclate the frame if the primary input is an image
Some(ty) if ty == concrete!(ImageFrame) => {
Some(ty) if ty == concrete!(ImageFrame<Color>) => {
// Calculate the size of the region to be exported
let old_transforms = self.remove_document_transform();
let transform = self.document_legacy.multiply_transforms(&layer_path).unwrap();

View File

@@ -113,8 +113,8 @@ fn static_nodes() -> Vec<DocumentNodeType> {
nodes: [
DocumentNode {
name: "Downres".to_string(),
inputs: vec![NodeInput::Network(concrete!(ImageFrame))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::raster::DownresNode")),
inputs: vec![NodeInput::Network(concrete!(ImageFrame<Color>))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::raster::DownresNode<_>")),
metadata: Default::default(),
},
DocumentNode {
@@ -160,7 +160,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
outputs: vec![NodeOutput::new(0, 0), NodeOutput::new(1, 0)],
nodes: [DocumentNode {
name: "Identity".to_string(),
inputs: vec![NodeInput::Network(concrete!(ImageFrame))],
inputs: vec![NodeInput::Network(concrete!(ImageFrame<Color>))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::IdNode")),
metadata: Default::default(),
}]
@@ -174,7 +174,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentInputType {
name: "In",
data_type: FrontendGraphDataType::General,
default: NodeInput::Network(concrete!(ImageFrame)),
default: NodeInput::Network(concrete!(ImageFrame<Color>)),
},
DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), false),
],
@@ -193,7 +193,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
nodes: [
DocumentNode {
name: "SetNode".to_string(),
inputs: vec![NodeInput::Network(concrete!(ImageFrame))],
inputs: vec![NodeInput::Network(concrete!(ImageFrame<Color>))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_core::ops::SomeNode")),
metadata: Default::default(),
},
@@ -277,7 +277,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentNodeType {
name: "Image Frame",
category: "General",
identifier: NodeImplementation::proto("graphene_std::raster::ImageFrameNode<_>"),
identifier: NodeImplementation::proto("graphene_std::raster::ImageFrameNode<_, _>"),
inputs: vec![
DocumentInputType::value("Image", TaggedValue::Image(Image::empty()), true),
DocumentInputType::value("Transform", TaggedValue::DAffine2(DAffine2::IDENTITY), true),
@@ -288,7 +288,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
DocumentNodeType {
name: "Mask",
category: "Image Adjustments",
identifier: NodeImplementation::proto("graphene_std::raster::MaskImageNode<_>"),
identifier: NodeImplementation::proto("graphene_std::raster::MaskImageNode<_, _, _>"),
inputs: vec![
DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true),
DocumentInputType::value("Stencil", TaggedValue::ImageFrame(ImageFrame::empty()), true),
@@ -419,7 +419,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
0,
DocumentNode {
name: "CacheNode".to_string(),
inputs: vec![NodeInput::Network(concrete!(Image))],
inputs: vec![NodeInput::Network(concrete!(Image<Color>))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
metadata: Default::default(),
},
@@ -489,7 +489,7 @@ fn static_nodes() -> Vec<DocumentNodeType> {
0,
DocumentNode {
name: "CacheNode".to_string(),
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::Network(concrete!(ImageFrame))],
inputs: vec![NodeInput::ShortCircut(concrete!(())), NodeInput::Network(concrete!(ImageFrame<Color>))],
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new("graphene_std::memo::CacheNode")),
metadata: Default::default(),
},

View File

@@ -954,7 +954,7 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
transform: glam::DAffine2::IDENTITY,
});
// Compute the transform input to the node graph frame
let image_frame: graphene_core::raster::ImageFrame = context.executor.compute_input(context.network, &imaginate_node, 0, image_frame).unwrap_or_default();
let image_frame: graphene_core::raster::ImageFrame<Color> = context.executor.compute_input(context.network, &imaginate_node, 0, image_frame).unwrap_or_default();
let transform = image_frame.transform;
let resolution = {

View File

@@ -205,7 +205,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
let r = (random_number >> 16) as u8;
let g = (random_number >> 8) as u8;
let b = random_number as u8;
let random_color = Color::from_rgba8(r, g, b, 255);
let random_color = Color::from_rgba8_srgb(r, g, b, 255);
document_data.primary_color = random_color;
document_data.update_working_colors(responses);

View File

@@ -12,6 +12,7 @@ use graph_craft::executor::Compiler;
use graph_craft::{concrete, Type, TypeDescriptor};
use graphene_core::raster::{Image, ImageFrame};
use graphene_core::vector::VectorData;
use graphene_core::Color;
use interpreted_executor::executor::DynamicExecutor;
use glam::{DAffine2, DVec2};
@@ -24,7 +25,7 @@ pub struct NodeGraphExecutor {
impl NodeGraphExecutor {
/// Execute the network by flattening it and creating a borrow stack.
fn execute_network<'a>(&'a mut self, network: NodeNetwork, image_frame: ImageFrame) -> Result<Box<dyn dyn_any::DynAny + 'a>, String> {
fn execute_network<'a>(&'a mut self, network: NodeNetwork, image_frame: ImageFrame<Color>) -> Result<Box<dyn dyn_any::DynAny + 'a>, String> {
let mut scoped_network = wrap_network_in_scope(network);
scoped_network.duplicate_outputs(&mut generate_uuid);
@@ -44,14 +45,14 @@ impl NodeGraphExecutor {
use graph_craft::executor::Executor;
match self.executor.input_type() {
Some(t) if t == concrete!(ImageFrame) => self.executor.execute(image_frame.into_dyn()).map_err(|e| e.to_string()),
Some(t) if t == concrete!(ImageFrame<Color>) => self.executor.execute(image_frame.into_dyn()).map_err(|e| e.to_string()),
Some(t) if t == concrete!(()) => self.executor.execute(().into_dyn()).map_err(|e| e.to_string()),
_ => Err("Invalid input type".to_string()),
}
}
/// Computes an input for a node in the graph
pub fn compute_input<T: dyn_any::StaticType>(&mut self, old_network: &NodeNetwork, node_path: &[NodeId], mut input_index: usize, image_frame: Cow<ImageFrame>) -> Result<T, String> {
pub fn compute_input<T: dyn_any::StaticType>(&mut self, old_network: &NodeNetwork, node_path: &[NodeId], mut input_index: usize, image_frame: Cow<ImageFrame<Color>>) -> Result<T, String> {
let mut network = old_network.clone();
// Adjust the output of the graph so we find the relevant output
'outer: for end in (0..node_path.len()).rev() {
@@ -93,7 +94,7 @@ impl NodeGraphExecutor {
}
/// Encodes an image into a format using the image crate
fn encode_img(image: Image, resize: Option<DVec2>, format: image::ImageOutputFormat) -> Result<(Vec<u8>, (u32, u32)), String> {
fn encode_img(image: Image<Color>, resize: Option<DVec2>, format: image::ImageOutputFormat) -> Result<(Vec<u8>, (u32, u32)), String> {
use image::{ImageBuffer, Rgba};
use std::io::Cursor;
@@ -118,7 +119,7 @@ impl NodeGraphExecutor {
imaginate_node: Vec<NodeId>,
(document, document_id): (&mut DocumentMessageHandler, u64),
layer_path: Vec<LayerId>,
image_frame: ImageFrame,
image_frame: ImageFrame<Color>,
(preferences, persistent_data): (&PreferencesMessageHandler, &PersistentData),
) -> Result<Message, String> {
use crate::messages::portfolio::document::node_graph::IMAGINATE_NODE;
@@ -153,8 +154,8 @@ impl NodeGraphExecutor {
};
let use_base_image = self.compute_input::<bool>(&network, &imaginate_node, get("Adapt Input Image"), Cow::Borrowed(&image_frame))?;
let input_image_frame: Option<ImageFrame> = if use_base_image {
Some(self.compute_input::<ImageFrame>(&network, &imaginate_node, get("Input Image"), Cow::Borrowed(&image_frame))?)
let input_image_frame: Option<ImageFrame<Color>> = if use_base_image {
Some(self.compute_input::<ImageFrame<Color>>(&network, &imaginate_node, get("Input Image"), Cow::Borrowed(&image_frame))?)
} else {
None
};