init
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "hcie-native"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
bincode = "1.3"
|
||||
hcie-protocol = { path = "../hcie-protocol" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["rlib"]
|
||||
@@ -0,0 +1,153 @@
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use hcie_protocol::{Layer, LayerType, LayerData, LayerStyle, BlendMode};
|
||||
|
||||
const HCIE_MAGIC: &[u8] = b"HCIE";
|
||||
const HCIE_VERSION: u32 = 1;
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
struct HcieLayerMeta {
|
||||
id: u64,
|
||||
name: String,
|
||||
layer_type: LayerType,
|
||||
data: LayerData,
|
||||
width: u32,
|
||||
height: u32,
|
||||
visible: bool,
|
||||
opacity: f32,
|
||||
blend_mode: BlendMode,
|
||||
locked: bool,
|
||||
parent_id: Option<u64>,
|
||||
styles: Vec<LayerStyle>,
|
||||
clipping_mask: bool,
|
||||
collapsed: bool,
|
||||
pixel_offset: u64,
|
||||
pixel_len: u64,
|
||||
#[serde(default)]
|
||||
effects: Vec<hcie_protocol::effects::LayerEffect>,
|
||||
#[serde(default = "default_fill_opacity")]
|
||||
fill_opacity: f32,
|
||||
}
|
||||
|
||||
fn default_fill_opacity() -> f32 { 1.0 }
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
struct HcieDocumentMeta {
|
||||
name: String,
|
||||
canvas_width: u32,
|
||||
canvas_height: u32,
|
||||
layers: Vec<HcieLayerMeta>,
|
||||
}
|
||||
|
||||
pub fn save_native(layers: &[Layer], path: &Path) -> Result<(), String> {
|
||||
use std::io::Write;
|
||||
|
||||
let mut file = std::fs::File::create(path).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut pixel_offset = 0u64;
|
||||
let mut layer_metas = Vec::with_capacity(layers.len());
|
||||
for layer in layers {
|
||||
let pixel_len = layer.pixels.len() as u64;
|
||||
layer_metas.push(HcieLayerMeta {
|
||||
id: layer.id,
|
||||
name: layer.name.clone(),
|
||||
layer_type: layer.layer_type,
|
||||
data: layer.data.clone(),
|
||||
width: layer.width,
|
||||
height: layer.height,
|
||||
visible: layer.visible,
|
||||
opacity: layer.opacity,
|
||||
blend_mode: layer.blend_mode,
|
||||
locked: layer.locked,
|
||||
parent_id: layer.parent_id,
|
||||
styles: layer.styles.clone(),
|
||||
clipping_mask: layer.clipping_mask,
|
||||
collapsed: layer.collapsed,
|
||||
pixel_offset,
|
||||
pixel_len,
|
||||
effects: layer.effects.clone(),
|
||||
fill_opacity: layer.fill_opacity,
|
||||
});
|
||||
pixel_offset += pixel_len;
|
||||
}
|
||||
|
||||
let doc_meta = HcieDocumentMeta {
|
||||
name: "Untitled".to_string(),
|
||||
canvas_width: layers.first().map(|l| l.width).unwrap_or(800),
|
||||
canvas_height: layers.first().map(|l| l.height).unwrap_or(600),
|
||||
layers: layer_metas,
|
||||
};
|
||||
|
||||
let json = serde_json::to_vec(&doc_meta).map_err(|e| e.to_string())?;
|
||||
let json_len = json.len() as u32;
|
||||
|
||||
file.write_all(HCIE_MAGIC).map_err(|e| e.to_string())?;
|
||||
file.write_all(&HCIE_VERSION.to_le_bytes()).map_err(|e| e.to_string())?;
|
||||
file.write_all(&json_len.to_le_bytes()).map_err(|e| e.to_string())?;
|
||||
file.write_all(&json).map_err(|e| e.to_string())?;
|
||||
|
||||
for layer in layers {
|
||||
file.write_all(&layer.pixels).map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_native(path: &Path) -> Result<Vec<Layer>, String> {
|
||||
let mut file = std::fs::File::open(path).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut magic = [0u8; 4];
|
||||
file.read_exact(&mut magic).map_err(|e| e.to_string())?;
|
||||
if &magic != HCIE_MAGIC {
|
||||
return Err("Not a valid HCIE file".to_string());
|
||||
}
|
||||
|
||||
let mut version_buf = [0u8; 4];
|
||||
file.read_exact(&mut version_buf).map_err(|e| e.to_string())?;
|
||||
let version = u32::from_le_bytes(version_buf);
|
||||
if version != HCIE_VERSION {
|
||||
return Err(format!("Unsupported HCIE version: {}", version));
|
||||
}
|
||||
|
||||
let mut json_len_buf = [0u8; 4];
|
||||
file.read_exact(&mut json_len_buf).map_err(|e| e.to_string())?;
|
||||
let json_len = u32::from_le_bytes(json_len_buf) as usize;
|
||||
|
||||
let mut json = vec![0u8; json_len];
|
||||
file.read_exact(&mut json).map_err(|e| e.to_string())?;
|
||||
let doc_meta: HcieDocumentMeta = serde_json::from_slice(&json).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut pixel_data = Vec::new();
|
||||
file.read_to_end(&mut pixel_data).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut layers = Vec::with_capacity(doc_meta.layers.len());
|
||||
for meta in doc_meta.layers {
|
||||
let offset = meta.pixel_offset as usize;
|
||||
let len = meta.pixel_len as usize;
|
||||
let pixels = if offset + len <= pixel_data.len() {
|
||||
pixel_data[offset..offset + len].to_vec()
|
||||
} else {
|
||||
vec![0u8; (meta.width * meta.height * 4) as usize]
|
||||
};
|
||||
|
||||
let mut layer = Layer::from_rgba(meta.name.clone(), meta.width, meta.height, pixels);
|
||||
layer.layer_type = meta.layer_type;
|
||||
layer.data = meta.data;
|
||||
layer.visible = meta.visible;
|
||||
layer.opacity = meta.opacity;
|
||||
layer.blend_mode = meta.blend_mode;
|
||||
layer.locked = meta.locked;
|
||||
layer.parent_id = meta.parent_id;
|
||||
layer.styles = meta.styles;
|
||||
layer.clipping_mask = meta.clipping_mask;
|
||||
layer.collapsed = meta.collapsed;
|
||||
layer.id = meta.id;
|
||||
layer.effects = meta.effects;
|
||||
layer.fill_opacity = meta.fill_opacity;
|
||||
layers.push(layer);
|
||||
}
|
||||
|
||||
Ok(layers)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user