use crate::gpu::params::{ParamDef, ParamValue};
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EngineState {
pub frame_count: f64,
pub thumbnail_version: u32,
pub dirty: bool,
pub has_selection: bool,
}
#[derive(serde::Serialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum LayerInfo {
#[serde(rename_all = "camelCase")]
Raster {
id: f64,
name: String,
visible: bool,
locked: bool,
editable: bool,
can_have_mask: bool,
can_rename: bool,
has_thumbnail: bool,
icon: &'static str,
kind_name: &'static str,
opacity: f32,
blend_mode: &'static str,
modifiers: Vec<ModifierInfo>,
bounds: crate::coord::CanvasRect,
},
#[serde(rename_all = "camelCase")]
Void {
id: f64,
name: String,
visible: bool,
locked: bool,
editable: bool,
can_have_mask: bool,
can_rename: bool,
has_thumbnail: bool,
icon: &'static str,
kind_name: &'static str,
opacity: f32,
blend_mode: &'static str,
modifiers: Vec<ModifierInfo>,
void_type: String,
params: Vec<ParamInfo>,
},
#[serde(rename_all = "camelCase")]
Filter {
id: f64,
name: String,
visible: bool,
locked: bool,
editable: bool,
can_have_mask: bool,
can_rename: bool,
has_thumbnail: bool,
icon: &'static str,
kind_name: &'static str,
opacity: f32,
blend_mode: &'static str,
modifiers: Vec<ModifierInfo>,
pipeline: String,
},
#[serde(rename_all = "camelCase")]
Group {
id: f64,
name: String,
visible: bool,
locked: bool,
editable: bool,
can_have_mask: bool,
can_rename: bool,
has_thumbnail: bool,
icon: &'static str,
kind_name: &'static str,
collapsed: bool,
passthrough: bool,
opacity: f32,
blend_mode: &'static str,
modifiers: Vec<ModifierInfo>,
children: Vec<LayerInfo>,
},
}
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ModifierInfo {
pub id: f64,
pub kind: &'static str,
pub name: String,
pub visible: bool,
pub locked: bool,
pub editable: bool,
}
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VeilTypeInfo {
#[serde(rename = "type")]
pub type_id: &'static str,
pub display_name: &'static str,
pub params: Vec<ParamInfo>,
}
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VoidTypeInfo {
#[serde(rename = "type")]
pub type_id: &'static str,
pub display_name: &'static str,
pub params: Vec<ParamInfo>,
pub icon: &'static str,
pub supports_preview: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub capture_kind: Option<crate::gpu::void::CaptureKind>,
}
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolTypeInfo {
#[serde(rename = "type")]
pub type_id: &'static str,
pub display_name: &'static str,
pub params: Vec<ParamInfo>,
}
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct BlendModeTypeInfo {
#[serde(rename = "type")]
pub type_id: &'static str,
pub display_name: &'static str,
pub category: &'static str,
}
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ModifierTypeInfo {
#[serde(rename = "type")]
pub type_id: &'static str,
pub display_name: &'static str,
}
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LayerKindTypeInfo {
#[serde(rename = "type")]
pub type_id: &'static str,
pub display_name: &'static str,
}
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VeilInfo {
#[serde(rename = "type")]
pub type_id: String,
pub visible: bool,
pub index: usize,
pub params: Vec<ParamInfo>,
}
#[derive(serde::Serialize)]
pub struct ParamInfo {
pub kind: &'static str,
pub name: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
pub min: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max: Option<f64>,
pub default: ParamValue,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<ParamValue>,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<serde_json::Value>,
}
impl ParamInfo {
pub fn from_def(def: &ParamDef, value: Option<&ParamValue>) -> Self {
match def {
ParamDef::Float {
name,
min,
max,
default,
} => ParamInfo {
kind: "float",
name,
min: Some(*min as f64),
max: Some(*max as f64),
default: ParamValue::Float(*default),
value: value.cloned(),
options: None,
},
ParamDef::Int {
name,
min,
max,
default,
} => ParamInfo {
kind: "int",
name,
min: Some(*min as f64),
max: Some(*max as f64),
default: ParamValue::Int(*default),
value: value.cloned(),
options: None,
},
ParamDef::Bool { name, default } => ParamInfo {
kind: "bool",
name,
min: None,
max: None,
default: ParamValue::Bool(*default),
value: value.cloned(),
options: None,
},
ParamDef::String { name, default } => ParamInfo {
kind: "string",
name,
min: None,
max: None,
default: ParamValue::String(default.to_string()),
value: value.cloned(),
options: None,
},
ParamDef::Curve { name, default } => ParamInfo {
kind: "curve",
name,
min: None,
max: None,
default: ParamValue::Curve(default.to_vec()),
value: value.cloned(),
options: None,
},
ParamDef::Enum {
name,
options,
default,
} => ParamInfo {
kind: "enum",
name,
min: None,
max: None,
default: ParamValue::Int(*default),
value: value.cloned(),
options: Some(serde_json::json!(options)),
},
ParamDef::FloatInput {
name,
min,
max,
default,
} => ParamInfo {
kind: "floatInput",
name,
min: Some(*min as f64),
max: Some(*max as f64),
default: ParamValue::Float(*default),
value: value.cloned(),
options: None,
},
ParamDef::Icon {
name,
options,
default,
} => ParamInfo {
kind: "icon",
name,
min: None,
max: None,
default: ParamValue::String(default.to_string()),
value: value.cloned(),
options: Some(serde_json::json!(options)),
},
}
}
}
#[derive(serde::Deserialize)]
#[serde(tag = "op", rename_all = "snake_case")]
pub enum StrokeOp {
FloodFill {
x: f32,
y: f32,
r: u8,
g: u8,
b: u8,
a: u8,
tolerance: u8,
},
LinearGradient {
x0: f32,
y0: f32,
x1: f32,
y1: f32,
r0: u8,
g0: u8,
b0: u8,
a0: u8,
r1: u8,
g1: u8,
b1: u8,
a1: u8,
},
BrushStroke {
x: f32,
y: f32,
pressure: f32,
x_tilt: f32,
y_tilt: f32,
rotation: f32,
tangential_pressure: f32,
time_ms: f64,
cr: f32,
cg: f32,
cb: f32,
ca: f32,
},
}
#[derive(serde::Serialize)]
pub struct ClipboardExport {
pub rgba: Vec<u8>,
pub width: u32,
pub height: u32,
pub offset_x: i32,
pub offset_y: i32,
}
pub(crate) fn node_to_layer_info(
doc: &crate::document::Document,
void_registry: &crate::gpu::void::VoidRegistry,
node_id: crate::layer::LayerId,
) -> Option<LayerInfo> {
use crate::layer::{Layer, LayerNode};
let node = doc.find_node(node_id)?;
let editable = doc.is_node_editable(node_id);
let kind = node.kind();
let info = match node {
LayerNode::Layer(layer) => match layer {
Layer::Raster(r) => LayerInfo::Raster {
id: r.id.to_ffi() as f64,
name: r.common.name.clone(),
visible: r.common.visible,
locked: r.common.locked,
editable,
can_have_mask: kind.can_have_mask,
can_rename: kind.can_rename,
has_thumbnail: kind.has_thumbnail,
icon: kind.icon,
kind_name: kind.display_name,
opacity: r.blend.opacity,
blend_mode: r.blend.blend_mode.type_id,
modifiers: r
.filters
.iter()
.filter_map(|mid| doc.find_filter(*mid).map(|m| modifier_to_info(doc, m)))
.collect(),
bounds: r.pixels.bounds,
},
Layer::Void(v) => {
let param_defs = void_registry.param_defs(&v.void_type);
let params = param_defs
.iter()
.enumerate()
.map(|(j, def)| ParamInfo::from_def(def, v.params.get(j)))
.collect();
let subtype_icon = void_registry.icon(&v.void_type);
LayerInfo::Void {
id: v.id.to_ffi() as f64,
name: v.common.name.clone(),
visible: v.common.visible,
locked: v.common.locked,
editable,
can_have_mask: kind.can_have_mask,
can_rename: kind.can_rename,
has_thumbnail: kind.has_thumbnail,
icon: if subtype_icon.is_empty() {
kind.icon
} else {
subtype_icon
},
kind_name: kind.display_name,
opacity: v.blend.opacity,
blend_mode: v.blend.blend_mode.type_id,
modifiers: v
.filters
.iter()
.filter_map(|mid| doc.find_filter(*mid).map(|m| modifier_to_info(doc, m)))
.collect(),
void_type: v.void_type.clone(),
params,
}
}
Layer::Filter(f) => LayerInfo::Filter {
id: f.id.to_ffi() as f64,
name: f.common.name.clone(),
visible: f.common.visible,
locked: f.common.locked,
editable,
can_have_mask: kind.can_have_mask,
can_rename: kind.can_rename,
has_thumbnail: kind.has_thumbnail,
icon: kind.icon,
kind_name: kind.display_name,
opacity: f.blend.opacity,
blend_mode: f.blend.blend_mode.type_id,
modifiers: f
.filters
.iter()
.filter_map(|mid| doc.find_filter(*mid).map(|m| modifier_to_info(doc, m)))
.collect(),
pipeline: f.pipeline.clone(),
},
},
LayerNode::Group(g) => LayerInfo::Group {
id: g.id.to_ffi() as f64,
name: g.common.name.clone(),
visible: g.common.visible,
locked: g.common.locked,
editable,
can_have_mask: kind.can_have_mask,
can_rename: kind.can_rename,
has_thumbnail: kind.has_thumbnail,
icon: kind.icon,
kind_name: kind.display_name,
collapsed: g.collapsed,
passthrough: g.passthrough,
opacity: g.blend.opacity,
blend_mode: g.blend.blend_mode.type_id,
modifiers: g
.filters
.iter()
.filter_map(|mid| doc.find_filter(*mid).map(|m| modifier_to_info(doc, m)))
.collect(),
children: g
.children
.iter()
.rev()
.filter_map(|cid| node_to_layer_info(doc, void_registry, *cid))
.collect(),
},
};
Some(info)
}
pub(crate) fn modifier_to_info(
doc: &crate::document::Document,
modifier: &crate::document::Filter,
) -> ModifierInfo {
ModifierInfo {
id: modifier.id.to_ffi() as f64,
kind: modifier.type_id(),
name: modifier.common.name.clone(),
visible: modifier.common.visible,
locked: modifier.common.locked,
editable: doc.is_node_editable(modifier.id),
}
}