Rename 'Image Value' node to 'Image' and have its input value be an image not a raster table (#4037)

* Rename 'Image Value' node to 'Image' and have its input value be Image<Color> not Table<Raster<CPU>>

* Add a Properties panel widget labeling "width x height" for images in the Image node

* Add Image<Color> node registry entry for MonitorNode

* Code review
This commit is contained in:
Keavon Chambers 2026-04-22 16:00:03 -07:00 committed by GitHub
parent c2e1208d82
commit d3f36a95cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 57 additions and 34 deletions

View File

@ -34,10 +34,8 @@ use graph_craft::document::{NodeId, NodeInput, NodeNetwork, OldNodeNetwork};
use graphene_std::math::quad::Quad;
use graphene_std::path_bool_nodes::boolean_intersect;
use graphene_std::raster::BlendMode;
use graphene_std::raster_types::Raster;
use graphene_std::render_node::wgpu_available;
use graphene_std::subpath::Subpath;
use graphene_std::table::Table;
use graphene_std::vector::PointId;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::misc::dvec2_to_point;
@ -696,7 +694,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
responses.add(DocumentMessage::AddTransaction);
let layer = graph_modification_utils::new_image_layer(Table::new_from_element(Raster::new_cpu(image)), layer_node_id, layer_parent, responses);
let layer = graph_modification_utils::new_image_layer(image, layer_node_id, layer_parent, responses);
if let Some(name) = name {
responses.add(NodeGraphMessage::SetDisplayName {

View File

@ -4,16 +4,16 @@ use crate::messages::portfolio::document::utility_types::network_interface::Node
use crate::messages::prelude::*;
use glam::{DAffine2, IVec2};
use graph_craft::document::NodeId;
use graphene_std::Artboard;
use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::color::Color;
use graphene_std::raster::BlendMode;
use graphene_std::raster_types::{CPU, Raster};
use graphene_std::raster_types::Image;
use graphene_std::subpath::Subpath;
use graphene_std::table::Table;
use graphene_std::text::{Font, TypesettingConfig};
use graphene_std::vector::PointId;
use graphene_std::vector::VectorModificationType;
use graphene_std::vector::style::{Fill, Stroke};
use graphene_std::{Artboard, Color};
#[impl_message(Message, DocumentMessage, GraphOperation)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
@ -70,7 +70,7 @@ pub enum GraphOperationMessage {
},
NewBitmapLayer {
id: NodeId,
image_frame: Table<Raster<CPU>>,
image: Image<Color>,
parent: LayerNodeIdentifier,
insert_index: usize,
},

View File

@ -160,15 +160,10 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for
responses.add_front(NodeGraphMessage::SelectedNodesSet { nodes: vec![id] });
responses.add(NodeGraphMessage::RunDocumentGraph);
}
GraphOperationMessage::NewBitmapLayer {
id,
image_frame,
parent,
insert_index,
} => {
GraphOperationMessage::NewBitmapLayer { id, image, parent, insert_index } => {
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
let layer = modify_inputs.create_layer(id);
modify_inputs.insert_image_data(image_frame, layer);
modify_inputs.insert_image_data(image, layer);
network_interface.move_layer_to_stack(layer, parent, insert_index, &[]);
responses.add(NodeGraphMessage::RunDocumentGraph);
}

View File

@ -9,7 +9,7 @@ use graph_craft::document::{NodeId, NodeInput};
use graph_craft::{ProtoNodeIdentifier, concrete};
use graphene_std::brush::brush_stroke::BrushStroke;
use graphene_std::raster::BlendMode;
use graphene_std::raster_types::{CPU, Raster};
use graphene_std::raster_types::Image;
use graphene_std::subpath::Subpath;
use graphene_std::table::Table;
use graphene_std::text::{Font, TypesettingConfig};
@ -303,14 +303,14 @@ impl<'a> ModifyInputsContext<'a> {
self.network_interface.move_node_to_chain_start(&color_value_id, layer, &[], self.import);
}
pub fn insert_image_data(&mut self, image_frame: Table<Raster<CPU>>, layer: LayerNodeIdentifier) {
pub fn insert_image_data(&mut self, image: Image<Color>, layer: LayerNodeIdentifier) {
let transform = resolve_network_node_type("Transform").expect("Transform node does not exist").default_node_template();
let image = resolve_proto_node_type(graphene_std::raster_nodes::std_nodes::image_value::IDENTIFIER)
.expect("ImageValue node does not exist")
.node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::Raster(image_frame), false))]);
let image_node = resolve_proto_node_type(graphene_std::raster_nodes::std_nodes::image::IDENTIFIER)
.expect("Image node does not exist")
.node_template_input_override([Some(NodeInput::value(TaggedValue::None, false)), Some(NodeInput::value(TaggedValue::ImageData(image), false))]);
let image_id = NodeId::new();
self.network_interface.insert_node(image_id, image, &[]);
self.network_interface.insert_node(image_id, image_node, &[]);
self.network_interface.move_node_to_chain_start(&image_id, layer, &[], self.import);
let transform_id = NodeId::new();

View File

@ -21,6 +21,7 @@ use graphene_std::raster::{
BlendMode, CellularDistanceFunction, CellularReturnType, Color, DomainWarpType, FractalType, LuminanceCalculation, NoiseType, RedGreenBlue, RedGreenBlueAlpha, RelativeAbsolute,
SelectiveColorChoice,
};
use graphene_std::raster_types::Image;
use graphene_std::table::{Table, TableRow};
use graphene_std::text::{Font, TextAlign};
use graphene_std::transform::{Footprint, ReferencePoint, ScaleType, Transform};
@ -231,6 +232,7 @@ pub(crate) fn property_from_type(
Some(x) if x == TypeId::of::<Curve>() => curve_widget(default_info),
Some(x) if x == TypeId::of::<Footprint>() => footprint_widget(default_info, &mut extra_widgets),
Some(x) if x == TypeId::of::<Box<VectorModification>>() => vector_modification_widget(default_info).into(),
Some(x) if x == TypeId::of::<Image<Color>>() => image_data_widget(default_info).into(),
// ===============================
// MANUALLY IMPLEMENTED ENUM TYPES
// ===============================
@ -420,6 +422,23 @@ pub fn vector_modification_widget(parameter_widgets_info: ParameterWidgetsInfo)
widgets
}
pub fn image_data_widget(parameter_widgets_info: ParameterWidgetsInfo) -> Vec<WidgetInstance> {
let ParameterWidgetsInfo { document_node, node_id: _, index, .. } = parameter_widgets_info;
let mut widgets = start_widgets(parameter_widgets_info);
let Some(document_node) = document_node else { return widgets };
let Some(input) = document_node.inputs.get(index) else { return widgets };
if let Some(TaggedValue::ImageData(image)) = input.as_non_exposed_value() {
let label = format!("{} x {}", image.width, image.height);
widgets.extend_from_slice(&[Separator::new(SeparatorStyle::Unrelated).widget_instance(), TextLabel::new(label).widget_instance()]);
}
widgets
}
pub fn footprint_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widgets: &mut Vec<LayoutGroup>) -> LayoutGroup {
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;

View File

@ -584,8 +584,9 @@ const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
],
},
NodeReplacement {
node: graphene_std::raster_nodes::std_nodes::image_value::IDENTIFIER,
node: graphene_std::raster_nodes::std_nodes::image::IDENTIFIER,
aliases: &[
"raster_nodes::std_nodes::ImageValueNode",
"graphene_raster_nodes::std_nodes::ImageValueNode",
"graphene_std::raster::ImageValueNode",
"graphene_std::raster::ImageNode",
@ -1384,7 +1385,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
.set_input(&InputConnector::node(NodeId(0), 1), NodeInput::value(TaggedValue::String(label), false), &[*node_id]);
}
if reference == DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::image_value::IDENTIFIER) && inputs_count == 1 {
if reference == DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::image::IDENTIFIER) && inputs_count == 1 {
let mut node_template = resolve_document_node_type(&reference)?.default_node_template();
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
@ -1972,6 +1973,19 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
}
}
// Migrate Image nodes that stored a Table<Raster<CPU>> in input 1 to instead use bare Image<Color> via TaggedValue::ImageData
if reference == DefinitionIdentifier::ProtoNode(graphene_std::raster_nodes::std_nodes::image::IDENTIFIER)
&& let Some(NodeInput::Value { tagged_value, .. }) = node.inputs.get(1)
&& let TaggedValue::Raster(raster_table) = &**tagged_value
&& let Some(row) = raster_table.iter().next()
{
let image = row.element.data().clone();
document
.network_interface
.set_input(&InputConnector::node(*node_id, 1), NodeInput::value(TaggedValue::ImageData(image), false), network_path);
}
// ==================================
// PUT ALL MIGRATIONS ABOVE THIS LINE
// ==================================

View File

@ -10,7 +10,7 @@ use graph_craft::{ProtoNodeIdentifier, concrete};
use graphene_std::Color;
use graphene_std::NodeInputDecleration;
use graphene_std::raster::BlendMode;
use graphene_std::raster_types::{CPU, GPU, Raster};
use graphene_std::raster_types::{CPU, GPU, Image, Raster};
use graphene_std::subpath::Subpath;
use graphene_std::table::Table;
use graphene_std::text::{Font, TypesettingConfig};
@ -218,14 +218,9 @@ pub fn new_vector_layer(subpaths: Vec<Subpath<PointId>>, id: NodeId, parent: Lay
}
/// Create a new bitmap layer.
pub fn new_image_layer(image_frame: Table<Raster<CPU>>, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
pub fn new_image_layer(image: Image<Color>, id: NodeId, parent: LayerNodeIdentifier, responses: &mut VecDeque<Message>) -> LayerNodeIdentifier {
let insert_index = 0;
responses.add(GraphOperationMessage::NewBitmapLayer {
id,
image_frame,
parent,
insert_index,
});
responses.add(GraphOperationMessage::NewBitmapLayer { id, image, parent, insert_index });
LayerNodeIdentifier::new_unchecked(id)
}

View File

@ -225,6 +225,7 @@ tagged_value! {
Curve(raster_nodes::curve::Curve),
Footprint(core_types::transform::Footprint),
VectorModification(Box<vector::VectorModification>),
ImageData(Image<Color>),
// ==========
// ENUM TYPES
// ==========

View File

@ -71,6 +71,7 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Raster<GPU>>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Table<GradientStops>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => Image<Color>]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => String]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => IVec2]),
async_node!(graphene_core::memo::MonitorNode<_, _, _>, input: Context, fn_params: [Context => DVec2]),

View File

@ -297,8 +297,8 @@ pub fn empty_image(_: impl Ctx, transform: DAffine2, color: Table<Color>) -> Tab
}
#[node_macro::node(category(""))]
pub fn image_value(_: impl Ctx, _primary: (), image: Table<Raster<CPU>>) -> Table<Raster<CPU>> {
image
pub fn image(_: impl Ctx, _primary: (), image: Image<Color>) -> Table<Raster<CPU>> {
Table::new_from_element(Raster::new_cpu(image))
}
#[node_macro::node(category("Raster: Pattern"))]