mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-17 07:18:04 +08:00
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:
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(),
|
||||
},
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user