mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 21:08:48 +08:00
Add support for layer groups in PSD files
This commit is contained in:
@@ -97,6 +97,215 @@ impl ScriptBuilder for PImgBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
struct PImgLayer<'a> {
|
||||
data: &'a PsbValueFixed,
|
||||
name: &'a str,
|
||||
layer_id: i64,
|
||||
/// seems is layer type in PSD files
|
||||
layer_type: i64,
|
||||
left: u32,
|
||||
top: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
opacity: u8,
|
||||
visible: bool,
|
||||
type_: i64,
|
||||
children: Vec<PImgLayer<'a>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PImgLayer<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PImgLayer")
|
||||
.field("layer_id", &self.layer_id)
|
||||
.field("layer_type", &self.layer_type)
|
||||
.field("name", &self.name)
|
||||
.field("left", &self.left)
|
||||
.field("top", &self.top)
|
||||
.field("width", &self.width)
|
||||
.field("height", &self.height)
|
||||
.field("opacity", &self.opacity)
|
||||
.field("visible", &self.visible)
|
||||
.field("type", &self.type_)
|
||||
.field("children", &self.children)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PImgLayer<'a> {
|
||||
pub fn new(data: &'a PsbValueFixed, layers: &'a PsbValueFixed) -> Result<Self> {
|
||||
let layer_id = data["layer_id"]
|
||||
.as_i64()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid layer_id"))?;
|
||||
let layer_type = data["layer_type"]
|
||||
.as_i64()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid layer_type"))?;
|
||||
let name = data["name"]
|
||||
.as_str()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid name"))?;
|
||||
let left = data["left"]
|
||||
.as_u32()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid left"))?;
|
||||
let top = data["top"]
|
||||
.as_u32()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid top"))?;
|
||||
let width = data["width"]
|
||||
.as_u32()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid width"))?;
|
||||
let height = data["height"]
|
||||
.as_u32()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid height"))?;
|
||||
let opacity = data["opacity"]
|
||||
.as_u8()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid opacity"))?;
|
||||
let visible = data["visible"].as_i64().unwrap_or(1) != 0;
|
||||
let type_ = data["type"]
|
||||
.as_i64()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid type"))?;
|
||||
let mut children = Vec::new();
|
||||
for layer in layers.members() {
|
||||
if layer_type == 2 || layer_type == 1 {
|
||||
if let Some(parent_id) = layer["group_layer_id"].as_i64() {
|
||||
if parent_id == layer_id {
|
||||
children.push(PImgLayer::new(layer, layers)?);
|
||||
}
|
||||
}
|
||||
} else if layer_type == 0 {
|
||||
if let Some(base_id) = layer["diff_id"].as_i64() {
|
||||
if base_id == layer_id {
|
||||
children.push(PImgLayer::new(layer, layers)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
data,
|
||||
layer_id,
|
||||
layer_type,
|
||||
name,
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
opacity,
|
||||
visible,
|
||||
type_,
|
||||
children,
|
||||
})
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
1 + self.children.iter().map(|c| c.len()).sum::<usize>()
|
||||
}
|
||||
|
||||
fn load_img(&self, img: &PImg) -> Result<ImageData> {
|
||||
if self.layer_type == 2 || self.layer_type == 1 {
|
||||
anyhow::bail!("Group layers do not have image data");
|
||||
}
|
||||
if self.layer_id == -1 {
|
||||
// Generate a empty image
|
||||
Ok(ImageData {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
color_type: ImageColorType::Rgba,
|
||||
depth: 8,
|
||||
data: vec![0u8; (self.width * self.height * 4) as usize],
|
||||
})
|
||||
} else {
|
||||
let tlg = img.load_img(self.layer_id).map_err(|e| {
|
||||
anyhow::anyhow!("Failed to load image for layer_id {}: {}", self.layer_id, e)
|
||||
})?;
|
||||
let mut img = ImageData {
|
||||
width: tlg.width,
|
||||
height: tlg.height,
|
||||
color_type: match tlg.color {
|
||||
TlgColorType::Bgr24 => ImageColorType::Bgr,
|
||||
TlgColorType::Bgra32 => ImageColorType::Bgra,
|
||||
TlgColorType::Grayscale8 => ImageColorType::Grayscale,
|
||||
},
|
||||
depth: 8,
|
||||
data: tlg.data.clone(),
|
||||
};
|
||||
convert_to_rgba(&mut img)?;
|
||||
Ok(img)
|
||||
}
|
||||
}
|
||||
|
||||
fn save_to_psd(&self, img: &PImg, psd: &mut PsdWriter, base: &mut ImageData) -> Result<()> {
|
||||
if self.children.is_empty() {
|
||||
let img = self.load_img(img)?;
|
||||
let mut visible = self.visible;
|
||||
if !self.data["diff_id"].is_none() {
|
||||
visible = false; // Diff layers are always hide by default
|
||||
}
|
||||
if visible {
|
||||
draw_on_img_with_opacity(base, &img, self.left, self.top, self.opacity)?;
|
||||
}
|
||||
let option = PsdLayerOption {
|
||||
visible,
|
||||
opacity: self.opacity,
|
||||
};
|
||||
psd.add_layer(self.name, self.left, self.top, img, Some(option))?;
|
||||
} else {
|
||||
psd.add_layer_group_end()?;
|
||||
if self.layer_type == 0 {
|
||||
let img = self.load_img(img)?;
|
||||
let visible = self.visible;
|
||||
if visible {
|
||||
draw_on_img_with_opacity(base, &img, self.left, self.top, self.opacity)?;
|
||||
}
|
||||
let option = PsdLayerOption {
|
||||
visible,
|
||||
opacity: self.opacity,
|
||||
};
|
||||
psd.add_layer(self.name, self.left, self.top, img, Some(option))?;
|
||||
}
|
||||
for child in &self.children {
|
||||
child.save_to_psd(img, psd, base)?;
|
||||
}
|
||||
let option = if self.layer_type == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(PsdLayerOption {
|
||||
visible: self.visible,
|
||||
opacity: self.opacity,
|
||||
})
|
||||
};
|
||||
psd.add_layer_group(self.name, self.layer_type == 2, option)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PImgLayerRoot<'a> {
|
||||
layers: Vec<PImgLayer<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> PImgLayerRoot<'a> {
|
||||
pub fn new(layers: &'a PsbValueFixed) -> Result<Self> {
|
||||
let mut root_layers = Vec::new();
|
||||
for layer in layers.members() {
|
||||
if layer["group_layer_id"].is_none() && layer["diff_id"].is_none() {
|
||||
root_layers.push(PImgLayer::new(layer, layers)?);
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
layers: root_layers,
|
||||
})
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.layers.iter().map(|l| l.len()).sum()
|
||||
}
|
||||
|
||||
fn save_to_psd(&self, img: &PImg, psd: &mut PsdWriter, base: &mut ImageData) -> Result<()> {
|
||||
for layer in &self.layers {
|
||||
layer.save_to_psd(img, psd, base)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Emote PImg Script
|
||||
pub struct PImg {
|
||||
@@ -226,7 +435,7 @@ impl Script for PImg {
|
||||
let height = psb["height"]
|
||||
.as_u32()
|
||||
.ok_or(anyhow::anyhow!("missing height"))?;
|
||||
let mut psd = PsdWriter::new(width, height, ImageColorType::Rgba, 8)?
|
||||
let mut psd = PsdWriter::new(width, height, ImageColorType::Rgba, 8, encoding)?
|
||||
.compress(self.psd_compress)
|
||||
.zlib_compression_level(self.zlib_compression_level);
|
||||
let mut base = ImageData {
|
||||
@@ -236,75 +445,14 @@ impl Script for PImg {
|
||||
depth: 8,
|
||||
data: vec![0u8; (width * height * 4) as usize],
|
||||
};
|
||||
let mut new_layers = PsbListFixed::new();
|
||||
for layer in psb["layers"].members() {
|
||||
if layer["diff_id"].is_none() {
|
||||
new_layers.values.push(layer.clone());
|
||||
}
|
||||
}
|
||||
for layer in psb["layers"].members() {
|
||||
if !layer["diff_id"].is_none() {
|
||||
new_layers.values.push(layer.clone());
|
||||
}
|
||||
}
|
||||
for layer in new_layers.iter() {
|
||||
let layer_id = layer["layer_id"]
|
||||
.as_i64()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid layer_id"))?;
|
||||
let layer_name = layer["name"]
|
||||
.as_str()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid name"))?;
|
||||
let width = layer["width"]
|
||||
.as_u32()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid width"))?;
|
||||
let height = layer["height"]
|
||||
.as_u32()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid height"))?;
|
||||
let top = layer["top"]
|
||||
.as_u32()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid top"))?;
|
||||
let left = layer["left"]
|
||||
.as_u32()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid left"))?;
|
||||
let opacity = layer["opacity"]
|
||||
.as_u8()
|
||||
.ok_or_else(|| anyhow::anyhow!("Layer does not have a valid opacity"))?;
|
||||
let mut visible = layer["visible"].as_u8().unwrap_or(1) != 0;
|
||||
if !layer["diff_id"].is_none() {
|
||||
visible = false; // Always hide diff layers
|
||||
}
|
||||
let img = self.load_img(layer_id)?;
|
||||
let mut layer = ImageData {
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
color_type: match img.color {
|
||||
TlgColorType::Bgr24 => ImageColorType::Bgr,
|
||||
TlgColorType::Bgra32 => ImageColorType::Bgra,
|
||||
TlgColorType::Grayscale8 => ImageColorType::Grayscale,
|
||||
},
|
||||
depth: 8,
|
||||
data: img.data.clone(),
|
||||
};
|
||||
if img.width != width || img.height != height {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Layer ID {} size mismatch: expected {}x{}, got {}x{}",
|
||||
layer_id,
|
||||
width,
|
||||
height,
|
||||
img.width,
|
||||
img.height
|
||||
));
|
||||
}
|
||||
convert_to_rgba(&mut layer)?;
|
||||
let option = PsdLayerOption { opacity, visible };
|
||||
if visible {
|
||||
draw_on_img_with_opacity(&mut base, &layer, left, top, opacity)?;
|
||||
}
|
||||
psd.add_layer(layer_name, left, top, layer, Some(option))?;
|
||||
let layers = PImgLayerRoot::new(&psb["layers"])?;
|
||||
if layers.len() != psb["layers"].len() {
|
||||
return Err(anyhow::anyhow!("Layer hierarchy is invalid"));
|
||||
}
|
||||
layers.save_to_psd(self, &mut psd, &mut base)?;
|
||||
let file = std::fs::File::create(filename)?;
|
||||
let mut writer = std::io::BufWriter::new(file);
|
||||
psd.save(base, &mut writer, encoding)?;
|
||||
psd.save(base, &mut writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,6 +227,7 @@ impl Script for DpngImage {
|
||||
self.img.header.image_height,
|
||||
ImageColorType::Rgba,
|
||||
8,
|
||||
encoding,
|
||||
)?
|
||||
.compress(self.config.psd_compress)
|
||||
.zlib_compression_level(self.config.zlib_compression_level);
|
||||
@@ -266,7 +267,7 @@ impl Script for DpngImage {
|
||||
}
|
||||
let file = std::fs::File::create(filename)?;
|
||||
let mut writer = std::io::BufWriter::new(file);
|
||||
psd.save(base, &mut writer, encoding)?;
|
||||
psd.save(base, &mut writer)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,9 +793,9 @@ pub fn draw_on_img_with_opacity(
|
||||
let src_comp = diff_pixel[i] as u16;
|
||||
let dst_comp = base_pixel_orig[i] as u16;
|
||||
|
||||
let numerator = src_comp * src_alpha_u16
|
||||
+ (dst_comp * dst_alpha_u16 * (255 - src_alpha_u16)) / 255;
|
||||
base.data[base_idx + i] = (numerator / out_alpha_u16) as u8;
|
||||
let numerator = src_comp as u32 * src_alpha_u16 as u32
|
||||
+ (dst_comp as u32 * dst_alpha_u16 as u32 * (255 - src_alpha_u16) as u32) / 255;
|
||||
base.data[base_idx + i] = (numerator / out_alpha_u16 as u32) as u8;
|
||||
}
|
||||
base.data[base_idx + 3] = out_alpha_u16 as u8;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ mod types;
|
||||
|
||||
use crate::ext::io::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::encoding::*;
|
||||
use crate::utils::img::*;
|
||||
use crate::utils::struct_pack::*;
|
||||
use anyhow::Result;
|
||||
@@ -35,11 +36,31 @@ pub struct PsdWriter {
|
||||
color_type: ImageColorType,
|
||||
compress: bool,
|
||||
zlib_compression_level: u32,
|
||||
encoding: Encoding,
|
||||
}
|
||||
|
||||
fn encode_unicode_layer(name: &str) -> Result<AdditionalLayerInfo> {
|
||||
let layer = UnicodeLayer {
|
||||
name: UnicodeString(name.to_string()),
|
||||
};
|
||||
let mut data = MemWriter::new();
|
||||
layer.pack(&mut data, true, Encoding::Utf16BE, &None)?;
|
||||
Ok(AdditionalLayerInfo {
|
||||
signature: *IMAGE_RESOURCE_SIGNATURE,
|
||||
key: *b"luni",
|
||||
data: data.into_inner(),
|
||||
})
|
||||
}
|
||||
|
||||
impl PsdWriter {
|
||||
/// Creates a new PSD writer with the specified dimensions, color type, and bit depth.
|
||||
pub fn new(width: u32, height: u32, color_type: ImageColorType, depth: u8) -> Result<Self> {
|
||||
pub fn new(
|
||||
width: u32,
|
||||
height: u32,
|
||||
color_type: ImageColorType,
|
||||
depth: u8,
|
||||
encoding: Encoding,
|
||||
) -> Result<Self> {
|
||||
let color_type = match color_type {
|
||||
ImageColorType::Bgr => ImageColorType::Rgb,
|
||||
ImageColorType::Bgra => ImageColorType::Rgba,
|
||||
@@ -91,6 +112,7 @@ impl PsdWriter {
|
||||
color_type,
|
||||
compress: true,
|
||||
zlib_compression_level: 6,
|
||||
encoding,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -215,12 +237,13 @@ impl PsdWriter {
|
||||
image_data: d,
|
||||
});
|
||||
}
|
||||
let encoded = encode_string(self.encoding, &name, false)?;
|
||||
let layer = LayerRecord {
|
||||
base: layer_base,
|
||||
layer_mask: None,
|
||||
layer_blending_ranges,
|
||||
layer_name: PascalString4(name.to_string()),
|
||||
infos: vec![],
|
||||
layer_name: PascalString4(encoded),
|
||||
infos: vec![encode_unicode_layer(name)?],
|
||||
};
|
||||
self.psd
|
||||
.layer_and_mask_info
|
||||
@@ -240,15 +263,115 @@ impl PsdWriter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds the start of a layer group to the PSD file.
|
||||
pub fn add_layer_group(
|
||||
&mut self,
|
||||
name: &str,
|
||||
is_closed: bool,
|
||||
option: Option<PsdLayerOption>,
|
||||
) -> Result<()> {
|
||||
let type_info = SectionDividerSetting {
|
||||
typ: if is_closed { 2 } else { 1 },
|
||||
};
|
||||
let mut data = MemWriter::new();
|
||||
type_info.pack(&mut data, true, self.encoding, &None)?;
|
||||
let encoded = encode_string(self.encoding, &name, false)?;
|
||||
let flags = if let Some(opt) = &option {
|
||||
opt.to_flags()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let opacity = if let Some(opt) = &option {
|
||||
opt.opacity
|
||||
} else {
|
||||
255
|
||||
};
|
||||
let layer = LayerRecord {
|
||||
base: LayerRecordBase {
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
channels: 0,
|
||||
channel_infos: vec![],
|
||||
blend_mode_signature: *IMAGE_RESOURCE_SIGNATURE,
|
||||
blend_mode_key: *b"pass",
|
||||
opacity,
|
||||
clipping: 0,
|
||||
flags,
|
||||
filler: 0,
|
||||
},
|
||||
layer_mask: None,
|
||||
layer_blending_ranges: LayerBlendingRanges {
|
||||
gray_blend_dest: 0xFFFF,
|
||||
gray_blend_source: 0xFFFF,
|
||||
channel_ranges: vec![],
|
||||
},
|
||||
layer_name: PascalString4(encoded),
|
||||
infos: vec![
|
||||
AdditionalLayerInfo {
|
||||
signature: *IMAGE_RESOURCE_SIGNATURE,
|
||||
key: *b"lsct",
|
||||
data: data.into_inner(),
|
||||
},
|
||||
encode_unicode_layer(name)?,
|
||||
],
|
||||
};
|
||||
self.psd
|
||||
.layer_and_mask_info
|
||||
.layer_info
|
||||
.layer_records
|
||||
.push(layer);
|
||||
self.psd.layer_and_mask_info.layer_info.layer_count += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds the end of a layer group to the PSD file.
|
||||
pub fn add_layer_group_end(&mut self) -> Result<()> {
|
||||
let type_info = SectionDividerSetting { typ: 3 };
|
||||
let mut data = MemWriter::new();
|
||||
type_info.pack(&mut data, true, self.encoding, &None)?;
|
||||
let layer = LayerRecord {
|
||||
base: LayerRecordBase {
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
channels: 0,
|
||||
channel_infos: vec![],
|
||||
blend_mode_signature: *IMAGE_RESOURCE_SIGNATURE,
|
||||
blend_mode_key: *b"norm",
|
||||
opacity: 255,
|
||||
clipping: 0,
|
||||
flags: 0,
|
||||
filler: 0,
|
||||
},
|
||||
layer_mask: None,
|
||||
layer_blending_ranges: LayerBlendingRanges {
|
||||
gray_blend_dest: 0xFFFF,
|
||||
gray_blend_source: 0xFFFF,
|
||||
channel_ranges: vec![],
|
||||
},
|
||||
layer_name: PascalString4(b"</Layer group>".to_vec()),
|
||||
infos: vec![AdditionalLayerInfo {
|
||||
signature: *IMAGE_RESOURCE_SIGNATURE,
|
||||
key: *b"lsct",
|
||||
data: data.into_inner(),
|
||||
}],
|
||||
};
|
||||
self.psd
|
||||
.layer_and_mask_info
|
||||
.layer_info
|
||||
.layer_records
|
||||
.push(layer);
|
||||
self.psd.layer_and_mask_info.layer_info.layer_count += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Saves the PSD file to the specified writer with the given encoding.
|
||||
///
|
||||
/// * `data` - The final composite image data to be saved in the PSD file.
|
||||
pub fn save<T: Write>(
|
||||
&mut self,
|
||||
data: ImageData,
|
||||
mut writer: T,
|
||||
encoding: Encoding,
|
||||
) -> Result<()> {
|
||||
pub fn save<T: Write>(&mut self, data: ImageData, mut writer: T) -> Result<()> {
|
||||
if data.color_type == ImageColorType::Bgr {
|
||||
convert_bgr_to_rgb(&mut data.clone())?;
|
||||
}
|
||||
@@ -382,7 +505,7 @@ impl PsdWriter {
|
||||
let compression = if self.compress { 1 } else { 0 };
|
||||
self.psd.image_data.image_data = planar_data;
|
||||
self.psd.image_data.compression = compression;
|
||||
self.psd.pack(&mut writer, true, encoding, &None)?;
|
||||
self.psd.pack(&mut writer, true, self.encoding, &None)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::ext::io::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::encoding::*;
|
||||
use crate::utils::struct_pack::*;
|
||||
use anyhow::Result;
|
||||
use msg_tool_macro::*;
|
||||
@@ -10,57 +9,51 @@ use std::io::{Read, Seek, Write};
|
||||
pub const PSD_SIGNATURE: &[u8; 4] = b"8BPS";
|
||||
pub const IMAGE_RESOURCE_SIGNATURE: &[u8; 4] = b"8BIM";
|
||||
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub struct UnicodeString(pub String);
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnicodeString(pub String);
|
||||
|
||||
// impl StructPack for UnicodeString {
|
||||
// fn pack<W: Write>(
|
||||
// &self,
|
||||
// writer: &mut W,
|
||||
// big: bool,
|
||||
// encoding: Encoding,
|
||||
// info: &Option<Box<dyn Any>>,
|
||||
// ) -> Result<()> {
|
||||
// let mut encoded: Vec<_> = self.0.encode_utf16().collect();
|
||||
// encoded.push(0); // null-terminator
|
||||
// let len = encoded.len() as u32;
|
||||
// len.pack(writer, big, encoding, info)?;
|
||||
// for c in encoded {
|
||||
// c.pack(writer, big, encoding, info)?;
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
impl StructPack for UnicodeString {
|
||||
fn pack<W: Write>(
|
||||
&self,
|
||||
writer: &mut W,
|
||||
big: bool,
|
||||
encoding: Encoding,
|
||||
info: &Option<Box<dyn Any>>,
|
||||
) -> Result<()> {
|
||||
let encoded: Vec<_> = self.0.encode_utf16().collect();
|
||||
let len = encoded.len() as u32;
|
||||
len.pack(writer, big, encoding, info)?;
|
||||
for c in encoded {
|
||||
c.pack(writer, big, encoding, info)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// impl StructUnpack for UnicodeString {
|
||||
// fn unpack<R: Read + Seek>(
|
||||
// reader: &mut R,
|
||||
// big: bool,
|
||||
// encoding: Encoding,
|
||||
// info: &Option<Box<dyn Any>>,
|
||||
// ) -> Result<Self> {
|
||||
// let len = u32::unpack(reader, big, encoding, info)?;
|
||||
// if len == 0 {
|
||||
// return Ok(UnicodeString(String::new()));
|
||||
// }
|
||||
// let mut encoded: Vec<u16> = Vec::with_capacity((len as usize) - 1);
|
||||
// for _ in 0..len - 1 {
|
||||
// let c = u16::unpack(reader, big, encoding, info)?;
|
||||
// encoded.push(c);
|
||||
// }
|
||||
// // null-terminator
|
||||
// let null = u16::unpack(reader, big, encoding, info)?;
|
||||
// if null != 0 {
|
||||
// return Err(anyhow::anyhow!("Expected null-terminator in UnicodeString"));
|
||||
// }
|
||||
// let string = String::from_utf16(&encoded)
|
||||
// .map_err(|e| anyhow::anyhow!("Failed to decode UTF-16 string: {}", e))?;
|
||||
// Ok(UnicodeString(string))
|
||||
// }
|
||||
// }
|
||||
impl StructUnpack for UnicodeString {
|
||||
fn unpack<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
big: bool,
|
||||
encoding: Encoding,
|
||||
info: &Option<Box<dyn Any>>,
|
||||
) -> Result<Self> {
|
||||
let len = u32::unpack(reader, big, encoding, info)?;
|
||||
if len == 0 {
|
||||
return Ok(UnicodeString(String::new()));
|
||||
}
|
||||
let mut encoded: Vec<u16> = Vec::with_capacity(len as usize);
|
||||
for _ in 0..len {
|
||||
let c = u16::unpack(reader, big, encoding, info)?;
|
||||
encoded.push(c);
|
||||
}
|
||||
let string = String::from_utf16(&encoded)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to decode UTF-16 string: {}", e))?;
|
||||
Ok(UnicodeString(string))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PascalString(pub String);
|
||||
pub struct PascalString(pub Vec<u8>);
|
||||
|
||||
impl StructPack for PascalString {
|
||||
fn pack<W: Write>(
|
||||
@@ -70,13 +63,12 @@ impl StructPack for PascalString {
|
||||
encoding: Encoding,
|
||||
info: &Option<Box<dyn Any>>,
|
||||
) -> Result<()> {
|
||||
let mut encoded = encode_string(encoding, &self.0, true)?;
|
||||
let len = encoded.len() as u8;
|
||||
let len = self.0.len() as u8;
|
||||
len.pack(writer, big, encoding, info)?;
|
||||
writer.write_all(&self.0)?;
|
||||
if len % 2 == 0 {
|
||||
encoded.push(0); // padding byte
|
||||
writer.write_u8(0)?; // padding byte
|
||||
}
|
||||
writer.write_all(&encoded)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -93,13 +85,12 @@ impl StructUnpack for PascalString {
|
||||
if len % 2 == 0 {
|
||||
reader.read_u8()?; // padding byte
|
||||
}
|
||||
let string = decode_to_string(encoding, &encoded, true)?;
|
||||
Ok(PascalString(string))
|
||||
Ok(PascalString(encoded))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PascalString4(pub String);
|
||||
pub struct PascalString4(pub Vec<u8>);
|
||||
|
||||
impl StructPack for PascalString4 {
|
||||
fn pack<W: Write>(
|
||||
@@ -109,16 +100,15 @@ impl StructPack for PascalString4 {
|
||||
encoding: Encoding,
|
||||
info: &Option<Box<dyn Any>>,
|
||||
) -> Result<()> {
|
||||
let mut encoded = encode_string(encoding, &self.0, true)?;
|
||||
let len = encoded.len() as u8;
|
||||
let len = self.0.len() as u8;
|
||||
len.pack(writer, big, encoding, info)?;
|
||||
let padding = 4 - (len as usize + 1) % 4;
|
||||
writer.write_all(&self.0)?;
|
||||
if padding != 4 {
|
||||
for _ in 0..padding {
|
||||
encoded.push(0); // padding bytes
|
||||
writer.write_u8(0)?; // padding byte
|
||||
}
|
||||
}
|
||||
writer.write_all(&encoded)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -144,8 +134,7 @@ impl StructUnpack for PascalString4 {
|
||||
}
|
||||
}
|
||||
}
|
||||
let string = decode_to_string(encoding, &encoded, true)?;
|
||||
Ok(PascalString4(string))
|
||||
Ok(PascalString4(encoded))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,12 +310,13 @@ impl StructPack for LayerInfo {
|
||||
for layer_record in &self.layer_records {
|
||||
layer_record.pack(&mut mem, big, encoding, info)?;
|
||||
}
|
||||
let mut index = 0usize;
|
||||
for i in 0..self.layer_count {
|
||||
let layer = &self.layer_records[i as usize];
|
||||
let info = Some(Box::new(layer.clone()) as Box<dyn Any>);
|
||||
for j in 0..layer.base.channels {
|
||||
let index = (i as usize) * (layer.base.channels as usize) + (j as usize);
|
||||
for _ in 0..layer.base.channels {
|
||||
let data = &self.channel_image_data[index];
|
||||
index += 1;
|
||||
data.pack(&mut mem, big, encoding, &info)?;
|
||||
}
|
||||
}
|
||||
@@ -931,3 +921,14 @@ impl StructPack for PsdFile {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, StructPack, StructUnpack)]
|
||||
pub struct SectionDividerSetting {
|
||||
pub typ: u32,
|
||||
// TODO: implement the rest fields
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, StructPack, StructUnpack)]
|
||||
pub struct UnicodeLayer {
|
||||
pub name: UnicodeString,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user