Add support to export pimg file as PSD file

This commit is contained in:
2026-02-03 11:22:20 +08:00
parent 151c71d5e9
commit 8b2eef1028
10 changed files with 194 additions and 14 deletions

View File

@@ -9,6 +9,26 @@ use anyhow::Result;
use std::io::Write;
use types::*;
#[derive(Debug, Clone, msg_tool_macro::Default)]
pub struct PsdLayerOption {
#[default(true)]
/// Whether the layer is visible.
pub visible: bool,
#[default(255)]
/// The opacity of the layer (0-255).
pub opacity: u8,
}
impl PsdLayerOption {
fn to_flags(&self) -> u8 {
let mut flags = 0u8;
if !self.visible {
flags |= 0b0000_0010;
}
flags
}
}
/// A simple PSD writer.
pub struct PsdWriter {
psd: PsdFile,
@@ -86,8 +106,21 @@ impl PsdWriter {
self
}
/// Add a visible layer to the PSD file.
pub fn add_layer(&mut self, name: &str, x: u32, y: u32, mut data: ImageData) -> Result<()> {
/// Add a layer to the PSD file.
///
/// * `name` - The name of the layer.
/// * `x` - The x position of the layer.
/// * `y` - The y position of the layer.
/// * `data` - The image data of the layer.
/// * `option` - The options for the layer.
pub fn add_layer(
&mut self,
name: &str,
x: u32,
y: u32,
mut data: ImageData,
option: Option<PsdLayerOption>,
) -> Result<()> {
if data.color_type == ImageColorType::Bgr {
convert_bgr_to_rgb(&mut data)?;
}
@@ -106,6 +139,16 @@ impl PsdWriter {
channel_ids.push(-1); // Alpha
}
}
let flags = if let Some(opt) = &option {
opt.to_flags()
} else {
0
};
let opacity = if let Some(opt) = &option {
opt.opacity
} else {
255
};
let mut layer_base = LayerRecordBase {
top: y as i32,
left: x as i32,
@@ -115,9 +158,9 @@ impl PsdWriter {
channel_infos: Vec::new(),
blend_mode_signature: *IMAGE_RESOURCE_SIGNATURE,
blend_mode_key: *b"norm",
opacity: 255,
opacity,
clipping: 0,
flags: 1,
flags,
filler: 0,
};
let mut channel_ranges = Vec::new();

View File

@@ -112,8 +112,11 @@ impl StructPack for PascalString4 {
let mut encoded = encode_string(encoding, &self.0, true)?;
let len = encoded.len() as u8;
len.pack(writer, big, encoding, info)?;
while (len as usize + 1) % 4 != 0 {
encoded.push(0); // padding bytes
let padding = 4 - (len as usize + 1) % 4;
if padding != 4 {
for _ in 0..padding {
encoded.push(0); // padding bytes
}
}
writer.write_all(&encoded)?;
Ok(())
@@ -129,8 +132,17 @@ impl StructUnpack for PascalString4 {
) -> Result<Self> {
let len = u8::unpack(reader, big, encoding, info)?;
let encoded = reader.read_exact_vec(len as usize)?;
while (len as usize + 1) % 4 != 0 {
reader.read_u8()?; // padding bytes
let padding = 4 - (len as usize + 1) % 4;
if padding != 4 {
for _ in 0..padding {
let pad_byte = reader.read_u8()?;
if pad_byte != 0 {
return Err(anyhow::anyhow!(
"Expected padding byte to be 0, got {}",
pad_byte
));
}
}
}
let string = decode_to_string(encoding, &encoded, true)?;
Ok(PascalString4(string))