mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 12:58:45 +08:00
Add import psd support for Qlie tiled PNG image (.png)
This commit is contained in:
@@ -121,7 +121,7 @@ utils-crc32 = []
|
||||
utils-escape = ["fancy-regex"]
|
||||
utils-mmx = []
|
||||
utils-pcm = []
|
||||
utils-psd = ["image", "flate2"]
|
||||
utils-psd = ["image", "flate2", "utils-bit-stream"]
|
||||
utils-rc4 = []
|
||||
utils-serde-base64bytes = ["base64"]
|
||||
utils-str = []
|
||||
|
||||
@@ -218,7 +218,7 @@ msg-tool create -t <script-type> <input> <output>
|
||||
|---|---|---|---|---|---|---|---|---|---|---|
|
||||
| `qlie` | `qlie` | Qlie Engine Scenario script (.s) | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | |
|
||||
| `qlie-abmp10` / `qlie-abmp11` / `qlie-abmp12` | `qlie-img` | Qlie Abmp10/11/12 image (.b) | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | |
|
||||
| `qlie-dpng` | `qlie-img` | Qlie tiled PNG image (.png) | ❌ | ❌ | ❌ | ❌ | ✔️ | ❌ | ❌ | `--qlie-dpng-psd` is required. |
|
||||
| `qlie-dpng` | `qlie-img` | Qlie tiled PNG image (.png) | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ❌ | `--qlie-dpng-psd` is required. |
|
||||
|
||||
| Archive Type | Feature Name | Name | Unpack | Pack | Remarks |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
@@ -270,6 +270,50 @@ impl Script for DpngImage {
|
||||
psd.save(base, &mut writer)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn custom_import<'a>(
|
||||
&'a self,
|
||||
custom_filename: &'a str,
|
||||
mut file: Box<dyn WriteSeek + 'a>,
|
||||
encoding: Encoding,
|
||||
output_encoding: Encoding,
|
||||
) -> Result<()> {
|
||||
let rfile = std::fs::File::open(custom_filename)?;
|
||||
let mut reader = std::io::BufReader::new(rfile);
|
||||
let psd = PsdReader::new(&mut reader, output_encoding)?;
|
||||
let width = psd.width();
|
||||
let height = psd.height();
|
||||
let layers = psd.read_normal_layers()?;
|
||||
let header = DpngHeader {
|
||||
magic: *b"DPNG",
|
||||
_unk1: 1,
|
||||
tile_count: layers.len() as u32,
|
||||
image_width: width,
|
||||
image_height: height,
|
||||
};
|
||||
let mut tiles = Vec::new();
|
||||
for layer in layers {
|
||||
let data = layer.image()?;
|
||||
let width = data.width;
|
||||
let height = data.height;
|
||||
let mut png_data = MemWriter::new();
|
||||
encode_img_writer(data, ImageOutputType::Png, &mut png_data, &self.config)?;
|
||||
let png_data = png_data.into_inner();
|
||||
let tile = Tile {
|
||||
x: layer.left() as u32,
|
||||
y: layer.top() as u32,
|
||||
width,
|
||||
height,
|
||||
size: png_data.len() as u32,
|
||||
_unk: 0,
|
||||
png_data,
|
||||
};
|
||||
tiles.push(tile);
|
||||
}
|
||||
let dpng = DpngFile { header, tiles };
|
||||
dpng.pack(&mut file, false, encoding, &None)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn create_raw_png_image<'a>(
|
||||
|
||||
175
src/utils/psd/compression.rs
Normal file
175
src/utils/psd/compression.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
use super::NormalLayer;
|
||||
use super::types::*;
|
||||
use crate::ext::io::*;
|
||||
use anyhow::Result;
|
||||
use std::io::Read;
|
||||
|
||||
pub fn rle_compress(data: &[u8]) -> Vec<u8> {
|
||||
let start = 0;
|
||||
let line_end = data.len();
|
||||
let mut idx = start;
|
||||
let mut literal: Vec<u8> = Vec::new();
|
||||
let mut out_line: Vec<u8> = Vec::new();
|
||||
while idx < line_end {
|
||||
// detect run length at current position
|
||||
let mut run_len = 1;
|
||||
while idx + run_len < line_end && data[idx + run_len] == data[idx] && run_len < 128 {
|
||||
run_len += 1;
|
||||
}
|
||||
|
||||
if run_len >= 3 {
|
||||
// flush any pending literals
|
||||
if !literal.is_empty() {
|
||||
// header = literal_len - 1 (0..127)
|
||||
let header = (literal.len() - 1) as i8;
|
||||
out_line.push(header as u8);
|
||||
out_line.extend_from_slice(&literal);
|
||||
literal.clear();
|
||||
}
|
||||
// write run: header = -(run_len - 1), then single byte value
|
||||
let header = -(((run_len as u8) - 1) as i8);
|
||||
out_line.push(header as u8);
|
||||
out_line.push(data[idx]);
|
||||
idx += run_len;
|
||||
} else {
|
||||
// collect literal bytes until a run of >=3 or 128 reached
|
||||
literal.push(data[idx]);
|
||||
idx += 1;
|
||||
// if literal is full, flush it
|
||||
if literal.len() == 128 {
|
||||
let header = (literal.len() - 1) as i8;
|
||||
out_line.push(header as u8);
|
||||
out_line.extend_from_slice(&literal);
|
||||
literal.clear();
|
||||
} else {
|
||||
// peek ahead: if next starts a run >=3, flush literal now
|
||||
if idx < line_end {
|
||||
let mut look_run = 1;
|
||||
while idx + look_run < line_end
|
||||
&& data[idx + look_run] == data[idx]
|
||||
&& look_run < 128
|
||||
{
|
||||
look_run += 1;
|
||||
}
|
||||
if look_run >= 3 {
|
||||
if !literal.is_empty() {
|
||||
let header = (literal.len() - 1) as i8;
|
||||
out_line.push(header as u8);
|
||||
out_line.extend_from_slice(&literal);
|
||||
literal.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// flush remaining literal
|
||||
if !literal.is_empty() {
|
||||
let header = (literal.len() - 1) as i8;
|
||||
out_line.push(header as u8);
|
||||
out_line.extend_from_slice(&literal);
|
||||
literal.clear();
|
||||
}
|
||||
out_line
|
||||
}
|
||||
|
||||
pub fn rle_decompress(data: &[u8]) -> Result<Vec<u8>> {
|
||||
let mut reader = MemReaderRef::new(data);
|
||||
let len = data.len();
|
||||
let mut out = Vec::new();
|
||||
while reader.pos < len {
|
||||
let c = reader.read_i8()?;
|
||||
if c >= 0 {
|
||||
let rlen = (c as usize) + 1;
|
||||
let old_len = out.len();
|
||||
out.resize(old_len + rlen, 0);
|
||||
reader.read_exact(&mut out[old_len..old_len + rlen])?;
|
||||
} else {
|
||||
let rlen = (-(c as isize) as usize) + 1;
|
||||
let val = reader.read_u8()?;
|
||||
let old_len = out.len();
|
||||
out.resize(old_len + rlen, val);
|
||||
}
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
pub fn decompress_channel_image_data(
|
||||
data: &mut ChannelImageData,
|
||||
layer: &NormalLayer,
|
||||
) -> Result<()> {
|
||||
match data.compression {
|
||||
0 => Ok(()), // no compression
|
||||
1 => {
|
||||
let mut reader = MemReaderRef::new(&data.image_data);
|
||||
let base = &layer.layer.base;
|
||||
let height = (base.bottom - base.top) as u32;
|
||||
let length_len = height as usize * 2;
|
||||
let mut start = length_len;
|
||||
let mut image = Vec::new();
|
||||
for _ in 0..height {
|
||||
let len = reader.read_u16_be()? as usize;
|
||||
let decompressed = rle_decompress(&data.image_data[start..start + len])?;
|
||||
start += len;
|
||||
image.extend(decompressed);
|
||||
}
|
||||
data.image_data = image;
|
||||
data.compression = 0;
|
||||
Ok(())
|
||||
}
|
||||
2 => {
|
||||
let mut decoder = flate2::read::ZlibDecoder::new(&data.image_data[..]);
|
||||
let mut decompressed = Vec::new();
|
||||
decoder.read_to_end(&mut decompressed)?;
|
||||
data.image_data = decompressed;
|
||||
data.compression = 0;
|
||||
Ok(())
|
||||
}
|
||||
3 => {
|
||||
let mut decoder = flate2::read::ZlibDecoder::new(&data.image_data[..]);
|
||||
let mut decompressed = Vec::new();
|
||||
decoder.read_to_end(&mut decompressed)?;
|
||||
let base = &layer.layer.base;
|
||||
let height = (base.bottom - base.top) as u32;
|
||||
let width = (base.right - base.left) as u32;
|
||||
let bit_depth = layer.psd.bit_depth();
|
||||
if bit_depth == 1 {
|
||||
anyhow::bail!("Decompression for 1-bit images is not implemented yet");
|
||||
}
|
||||
let mut writer = MemWriterRef::new(&mut decompressed);
|
||||
for _ in 0..height {
|
||||
if bit_depth == 8 {
|
||||
let mut pre = writer.read_u8()?;
|
||||
for _ in 1..width {
|
||||
let cur = writer.peek_u8()?;
|
||||
let val = cur.wrapping_add(pre);
|
||||
writer.write_u8(val)?;
|
||||
pre = val;
|
||||
}
|
||||
} else if bit_depth == 16 {
|
||||
let mut pre = writer.read_u16_be()?;
|
||||
for _ in 1..width {
|
||||
let cur = writer.peek_u16_be()?;
|
||||
let val = cur.wrapping_add(pre);
|
||||
writer.write_u16_be(val)?;
|
||||
pre = val;
|
||||
}
|
||||
} else if bit_depth == 32 {
|
||||
let mut pre = writer.read_u32_be()?;
|
||||
for _ in 1..width {
|
||||
let cur = writer.peek_u32_be()?;
|
||||
let val = cur.wrapping_add(pre);
|
||||
writer.write_u32_be(val)?;
|
||||
pre = val;
|
||||
}
|
||||
} else {
|
||||
anyhow::bail!("Unsupported bit depth for decompression: {}", bit_depth);
|
||||
}
|
||||
}
|
||||
data.image_data = decompressed;
|
||||
data.compression = 0;
|
||||
Ok(())
|
||||
}
|
||||
_ => anyhow::bail!("Unsupported compression type: {}", data.compression),
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
//! A simple PSD writer
|
||||
//! A simple PSD reader/writer
|
||||
mod compression;
|
||||
mod types;
|
||||
|
||||
use crate::ext::io::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::bit_stream::*;
|
||||
use crate::utils::encoding::*;
|
||||
use crate::utils::img::*;
|
||||
use crate::utils::struct_pack::*;
|
||||
use anyhow::Result;
|
||||
use std::io::Write;
|
||||
use compression::*;
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::ops::Deref;
|
||||
use types::*;
|
||||
|
||||
pub use types::{
|
||||
@@ -54,7 +59,7 @@ fn encode_unicode_layer(name: &str) -> Result<AdditionalLayerInfo> {
|
||||
layer.pack(&mut data, true, Encoding::Utf16BE, &None)?;
|
||||
Ok(AdditionalLayerInfo {
|
||||
signature: *IMAGE_RESOURCE_SIGNATURE,
|
||||
key: *b"luni",
|
||||
key: *UNICODE_LAYER_KEY,
|
||||
data: data.into_inner(),
|
||||
})
|
||||
}
|
||||
@@ -100,13 +105,13 @@ impl PsdWriter {
|
||||
layer_records: vec![],
|
||||
channel_image_data: vec![],
|
||||
},
|
||||
global_layer_mask_info: GlobalLayerMaskInfo {
|
||||
global_layer_mask_info: Some(GlobalLayerMaskInfo {
|
||||
overlays_color_space: 0,
|
||||
overlays_color_components: [0; 4],
|
||||
opacity: 0,
|
||||
kind: 128,
|
||||
filler: vec![0],
|
||||
},
|
||||
}),
|
||||
tagged_blocks: vec![],
|
||||
},
|
||||
image_data: ImageDataSection {
|
||||
@@ -263,8 +268,7 @@ impl PsdWriter {
|
||||
.push(layer);
|
||||
|
||||
// Update layer count
|
||||
self.psd.layer_and_mask_info.layer_info.layer_count =
|
||||
self.psd.layer_and_mask_info.layer_info.layer_records.len() as u16;
|
||||
self.psd.layer_and_mask_info.layer_info.layer_count += 1;
|
||||
|
||||
self.psd
|
||||
.layer_and_mask_info
|
||||
@@ -300,7 +304,7 @@ impl PsdWriter {
|
||||
let mut infos = vec![
|
||||
AdditionalLayerInfo {
|
||||
signature: *IMAGE_RESOURCE_SIGNATURE,
|
||||
key: *b"lsct",
|
||||
key: *SECTION_DIVIDER_SETTING_KEY,
|
||||
data: data.into_inner(),
|
||||
},
|
||||
encode_unicode_layer(name)?,
|
||||
@@ -370,7 +374,7 @@ impl PsdWriter {
|
||||
layer_name: PascalString4(b"</Layer group>".to_vec()),
|
||||
infos: vec![AdditionalLayerInfo {
|
||||
signature: *IMAGE_RESOURCE_SIGNATURE,
|
||||
key: *b"lsct",
|
||||
key: *SECTION_DIVIDER_SETTING_KEY,
|
||||
data: data.into_inner(),
|
||||
}],
|
||||
};
|
||||
@@ -440,74 +444,7 @@ impl PsdWriter {
|
||||
for y in 0..height {
|
||||
let start = (c * width * height) + (y * width);
|
||||
let line_end = start + width;
|
||||
let mut idx = start;
|
||||
let mut literal: Vec<u8> = Vec::new();
|
||||
let mut out_line: Vec<u8> = Vec::new();
|
||||
|
||||
while idx < line_end {
|
||||
// detect run length at current position
|
||||
let mut run_len = 1;
|
||||
while idx + run_len < line_end
|
||||
&& planar_data[idx + run_len] == planar_data[idx]
|
||||
&& run_len < 128
|
||||
{
|
||||
run_len += 1;
|
||||
}
|
||||
|
||||
if run_len >= 3 {
|
||||
// flush any pending literals
|
||||
if !literal.is_empty() {
|
||||
// header = literal_len - 1 (0..127)
|
||||
let header = (literal.len() - 1) as i8;
|
||||
out_line.push(header as u8);
|
||||
out_line.extend_from_slice(&literal);
|
||||
literal.clear();
|
||||
}
|
||||
// write run: header = -(run_len - 1), then single byte value
|
||||
let header = -(((run_len as u8) - 1) as i8);
|
||||
out_line.push(header as u8);
|
||||
out_line.push(planar_data[idx]);
|
||||
idx += run_len;
|
||||
} else {
|
||||
// collect literal bytes until a run of >=3 or 128 reached
|
||||
literal.push(planar_data[idx]);
|
||||
idx += 1;
|
||||
// if literal is full, flush it
|
||||
if literal.len() == 128 {
|
||||
let header = (literal.len() - 1) as i8;
|
||||
out_line.push(header as u8);
|
||||
out_line.extend_from_slice(&literal);
|
||||
literal.clear();
|
||||
} else {
|
||||
// peek ahead: if next starts a run >=3, flush literal now
|
||||
if idx < line_end {
|
||||
let mut look_run = 1;
|
||||
while idx + look_run < line_end
|
||||
&& planar_data[idx + look_run] == planar_data[idx]
|
||||
&& look_run < 128
|
||||
{
|
||||
look_run += 1;
|
||||
}
|
||||
if look_run >= 3 {
|
||||
if !literal.is_empty() {
|
||||
let header = (literal.len() - 1) as i8;
|
||||
out_line.push(header as u8);
|
||||
out_line.extend_from_slice(&literal);
|
||||
literal.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flush remaining literal
|
||||
if !literal.is_empty() {
|
||||
let header = (literal.len() - 1) as i8;
|
||||
out_line.push(header as u8);
|
||||
out_line.extend_from_slice(&literal);
|
||||
literal.clear();
|
||||
}
|
||||
let out_line = rle_compress(&planar_data[start..line_end]);
|
||||
|
||||
// write scanline length at reserved spot and append data
|
||||
compressed
|
||||
@@ -524,3 +461,409 @@ impl PsdWriter {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PsdReader {
|
||||
psd: PsdFile,
|
||||
encoding: Encoding,
|
||||
channel_start_indices: Vec<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NormalLayer<'a> {
|
||||
layer: &'a LayerRecord,
|
||||
layer_idx: usize,
|
||||
psd: &'a PsdReader,
|
||||
}
|
||||
|
||||
impl<'a> NormalLayer<'a> {
|
||||
/// Returns the name of the layer.
|
||||
pub fn layer_name(&self) -> Result<String> {
|
||||
self.layer.layer_name(self.psd.encoding)
|
||||
}
|
||||
|
||||
/// Returns the current layer's index in the PSD file.
|
||||
pub fn layer_index(&self) -> usize {
|
||||
self.layer_idx
|
||||
}
|
||||
|
||||
/// Returns the top position of the layer.
|
||||
pub fn top(&self) -> i32 {
|
||||
self.layer.base.top
|
||||
}
|
||||
|
||||
/// Returns the left position of the layer.
|
||||
pub fn left(&self) -> i32 {
|
||||
self.layer.base.left
|
||||
}
|
||||
|
||||
/// Returns the bottom position of the layer.
|
||||
pub fn bottom(&self) -> i32 {
|
||||
self.layer.base.bottom
|
||||
}
|
||||
|
||||
/// Returns the right position of the layer.
|
||||
pub fn right(&self) -> i32 {
|
||||
self.layer.base.right
|
||||
}
|
||||
|
||||
/// Returns the width of the layer.
|
||||
pub fn width(&self) -> u32 {
|
||||
(self.layer.base.right - self.layer.base.left) as u32
|
||||
}
|
||||
|
||||
/// Returns the height of the layer.
|
||||
pub fn height(&self) -> u32 {
|
||||
(self.layer.base.bottom - self.layer.base.top) as u32
|
||||
}
|
||||
|
||||
/// Returns the number of channels in the layer.
|
||||
pub fn channels(&self) -> u16 {
|
||||
self.layer.base.channels
|
||||
}
|
||||
|
||||
/// Reads and returns the raw channel id and data of the layer.
|
||||
pub fn read_raw_data(&self) -> Result<Vec<(i16, Vec<u8>)>> {
|
||||
let mut start_idx = self.psd.channel_start_indices[self.layer_idx];
|
||||
let mut channels_data = Vec::new();
|
||||
for cinfo in &self.layer.base.channel_infos {
|
||||
let mut data = self
|
||||
.psd
|
||||
.psd
|
||||
.layer_and_mask_info
|
||||
.layer_info
|
||||
.channel_image_data[start_idx]
|
||||
.clone();
|
||||
start_idx += 1;
|
||||
decompress_channel_image_data(&mut data, self)?;
|
||||
channels_data.push((cinfo.channel_id, data.image_data));
|
||||
}
|
||||
Ok(channels_data)
|
||||
}
|
||||
|
||||
/// Reads and returns the image data of the layer.
|
||||
pub fn image(&self) -> Result<ImageData> {
|
||||
let color_mode = self.psd.color_mode();
|
||||
if !matches!(color_mode, 1 | 3) {
|
||||
anyhow::bail!(
|
||||
"Unsupported PSD color mode for image extraction: {}",
|
||||
self.psd.color_mode()
|
||||
);
|
||||
}
|
||||
let channels = self.layer.base.channels;
|
||||
let width = (self.layer.base.right - self.layer.base.left) as u32;
|
||||
let height = (self.layer.base.bottom - self.layer.base.top) as u32;
|
||||
if channels < 1 {
|
||||
anyhow::bail!("PSD layer has no channels");
|
||||
}
|
||||
let channels_map = BTreeMap::from_iter(self.read_raw_data()?);
|
||||
if color_mode == 1 {
|
||||
let grayscale = channels_map
|
||||
.get(&0)
|
||||
.ok_or_else(|| anyhow::anyhow!("PSD grayscale layer missing channel 0"))?;
|
||||
let alpha = channels_map.get(&-1);
|
||||
let depth = self.psd.bit_depth() as u8;
|
||||
if let Some(alpha) = alpha {
|
||||
let mut g = MsbBitStream::new(MemReaderRef::new(&grayscale));
|
||||
let mut a = MsbBitStream::new(MemReaderRef::new(&alpha));
|
||||
let mut data = MemWriter::new();
|
||||
let mut o = MsbBitWriter::new(&mut data);
|
||||
for _ in 0..height {
|
||||
g.m_cached_bits = 0;
|
||||
a.m_cached_bits = 0;
|
||||
for _ in 0..width {
|
||||
let gray = g.get_bits(depth as u32)?;
|
||||
let alpha = a.get_bits(depth as u32)?;
|
||||
o.put_bits(gray, depth)?;
|
||||
o.put_bits(gray, depth)?;
|
||||
o.put_bits(gray, depth)?;
|
||||
o.put_bits(alpha, depth)?;
|
||||
}
|
||||
o.flush()?;
|
||||
}
|
||||
Ok(ImageData {
|
||||
width,
|
||||
height,
|
||||
color_type: ImageColorType::Rgba,
|
||||
depth,
|
||||
data: data.into_inner(),
|
||||
})
|
||||
} else {
|
||||
Ok(ImageData {
|
||||
width,
|
||||
height,
|
||||
color_type: ImageColorType::Grayscale,
|
||||
depth,
|
||||
data: grayscale.clone(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
let red = channels_map
|
||||
.get(&0)
|
||||
.ok_or_else(|| anyhow::anyhow!("PSD RGB layer missing channel 0"))?;
|
||||
let green = channels_map
|
||||
.get(&1)
|
||||
.ok_or_else(|| anyhow::anyhow!("PSD RGB layer missing channel 1"))?;
|
||||
let blue = channels_map
|
||||
.get(&2)
|
||||
.ok_or_else(|| anyhow::anyhow!("PSD RGB layer missing channel 2"))?;
|
||||
let mut a = channels_map
|
||||
.get(&-1)
|
||||
.map(|v| MsbBitStream::new(MemReaderRef::new(v)));
|
||||
let depth = self.psd.bit_depth() as u8;
|
||||
let mut r = MsbBitStream::new(MemReaderRef::new(&red));
|
||||
let mut g = MsbBitStream::new(MemReaderRef::new(&green));
|
||||
let mut b = MsbBitStream::new(MemReaderRef::new(&blue));
|
||||
let mut data = MemWriter::new();
|
||||
let mut o = MsbBitWriter::new(&mut data);
|
||||
for _ in 0..height {
|
||||
r.m_cached_bits = 0;
|
||||
g.m_cached_bits = 0;
|
||||
b.m_cached_bits = 0;
|
||||
if let Some(alpha) = &mut a {
|
||||
alpha.m_cached_bits = 0;
|
||||
}
|
||||
for _ in 0..width {
|
||||
let red = r.get_bits(depth as u32)?;
|
||||
let green = g.get_bits(depth as u32)?;
|
||||
let blue = b.get_bits(depth as u32)?;
|
||||
o.put_bits(red, depth)?;
|
||||
o.put_bits(green, depth)?;
|
||||
o.put_bits(blue, depth)?;
|
||||
if let Some(alpha) = &mut a {
|
||||
let alpha = alpha.get_bits(depth as u32)?;
|
||||
o.put_bits(alpha, depth)?;
|
||||
}
|
||||
}
|
||||
o.flush()?;
|
||||
}
|
||||
Ok(ImageData {
|
||||
width,
|
||||
height,
|
||||
color_type: if a.is_some() {
|
||||
ImageColorType::Rgba
|
||||
} else {
|
||||
ImageColorType::Rgb
|
||||
},
|
||||
depth,
|
||||
data: data.into_inner(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GroupLayer<'a> {
|
||||
layer: &'a LayerRecord,
|
||||
layer_idx: usize,
|
||||
psd: &'a PsdReader,
|
||||
pub childrens: Vec<Layer<'a>>,
|
||||
is_closed: bool,
|
||||
}
|
||||
|
||||
impl<'a> GroupLayer<'a> {
|
||||
fn create<'b>(layer_idx: &'b mut usize, psd: &'a PsdReader) -> Result<Self> {
|
||||
let mut childrens = Vec::new();
|
||||
// skip the end marker
|
||||
*layer_idx += 1;
|
||||
let layer_count = psd.psd.layer_count();
|
||||
while *layer_idx < layer_count {
|
||||
let layer = &psd.psd.layer_and_mask_info.layer_info.layer_records[*layer_idx];
|
||||
let layer_type = if let Some(lsct) = layer.get_info(SECTION_DIVIDER_SETTING_KEY) {
|
||||
let type_info = SectionDividerSetting::unpack(
|
||||
&mut MemReaderRef::new(lsct),
|
||||
true,
|
||||
psd.encoding,
|
||||
&None,
|
||||
)?;
|
||||
type_info.typ
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if layer_type == 1 || layer_type == 2 {
|
||||
let is_closed = layer_type == 2;
|
||||
return Ok(GroupLayer {
|
||||
layer,
|
||||
layer_idx: *layer_idx,
|
||||
psd,
|
||||
childrens,
|
||||
is_closed,
|
||||
});
|
||||
} else if layer_type == 3 {
|
||||
childrens.push(Layer::Group(GroupLayer::create(layer_idx, psd)?));
|
||||
} else if layer_type == 0 {
|
||||
childrens.push(Layer::Normal(NormalLayer {
|
||||
layer,
|
||||
layer_idx: *layer_idx,
|
||||
psd,
|
||||
}));
|
||||
} else {
|
||||
anyhow::bail!("Unknown layer section divider type: {}", layer_type);
|
||||
}
|
||||
*layer_idx += 1;
|
||||
}
|
||||
anyhow::bail!("Layer group does not have a start marker");
|
||||
}
|
||||
|
||||
/// Returns whether the layer group is closed.
|
||||
pub fn is_closed(&self) -> bool {
|
||||
self.is_closed
|
||||
}
|
||||
|
||||
/// Returns the name of the layer group.
|
||||
pub fn layer_name(&self) -> Result<String> {
|
||||
self.layer.layer_name(self.psd.encoding)
|
||||
}
|
||||
|
||||
/// Returns the current layer's index in the PSD file.
|
||||
pub fn layer_index(&self) -> usize {
|
||||
self.layer_idx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for GroupLayer<'a> {
|
||||
type Target = Vec<Layer<'a>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.childrens
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Layer<'a> {
|
||||
Normal(NormalLayer<'a>),
|
||||
Group(GroupLayer<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Layer<'a> {
|
||||
/// Returns the name of the layer.
|
||||
pub fn layer_name(&self) -> Result<String> {
|
||||
match self {
|
||||
Layer::Normal(n) => n.layer_name(),
|
||||
Layer::Group(g) => g.layer_name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current layer's index in the PSD file.
|
||||
pub fn layer_index(&self) -> usize {
|
||||
match self {
|
||||
Layer::Normal(n) => n.layer_index(),
|
||||
Layer::Group(g) => g.layer_index(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PsdReader {
|
||||
pub fn new<T: Read + Seek>(mut reader: T, encoding: Encoding) -> Result<Self> {
|
||||
let psd = PsdFile::unpack(&mut reader, true, encoding, &None)?;
|
||||
if psd.header.signature != *PSD_SIGNATURE {
|
||||
anyhow::bail!("Invalid PSD signature");
|
||||
}
|
||||
if psd.header.version != 1 {
|
||||
anyhow::bail!("Unsupported PSD version: {}", psd.header.version);
|
||||
}
|
||||
let mut channel_start_indices = Vec::new();
|
||||
let mut idx = 0;
|
||||
for layer in &psd.layer_and_mask_info.layer_info.layer_records {
|
||||
channel_start_indices.push(idx);
|
||||
idx += layer.base.channels as usize;
|
||||
}
|
||||
Ok(Self {
|
||||
psd,
|
||||
encoding,
|
||||
channel_start_indices,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the width of the PSD image.
|
||||
pub fn width(&self) -> u32 {
|
||||
self.psd.header.width
|
||||
}
|
||||
|
||||
/// Returns the height of the PSD image.
|
||||
pub fn height(&self) -> u32 {
|
||||
self.psd.header.height
|
||||
}
|
||||
|
||||
/// Returns the color mode of the PSD file.
|
||||
///
|
||||
/// 1 = Grayscale, 3 = RGB, etc.
|
||||
pub fn color_mode(&self) -> u16 {
|
||||
self.psd.header.color_mode
|
||||
}
|
||||
|
||||
/// Returns the number of channels in the PSD file.
|
||||
pub fn channels(&self) -> u16 {
|
||||
self.psd.header.channels
|
||||
}
|
||||
|
||||
/// Returns the bit depth of the PSD file.
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
self.psd.header.depth
|
||||
}
|
||||
|
||||
/// Reads and returns the layers in the PSD file.
|
||||
pub fn read_layers<'a>(&'a self) -> Result<Vec<Layer<'a>>> {
|
||||
let mut layers = Vec::new();
|
||||
let mut layer_idx = 0;
|
||||
let count = self.psd.layer_count();
|
||||
while layer_idx < count {
|
||||
let layer = &self.psd.layer_and_mask_info.layer_info.layer_records[layer_idx];
|
||||
let layer_type = if let Some(lsct) = layer.get_info(SECTION_DIVIDER_SETTING_KEY) {
|
||||
let type_info = SectionDividerSetting::unpack(
|
||||
&mut MemReaderRef::new(lsct),
|
||||
true,
|
||||
self.encoding,
|
||||
&None,
|
||||
)?;
|
||||
type_info.typ
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if layer_type == 1 || layer_type == 2 {
|
||||
anyhow::bail!("Layer group does not have an end marker");
|
||||
}
|
||||
if layer_type == 0 {
|
||||
layers.push(Layer::Normal(NormalLayer {
|
||||
layer,
|
||||
layer_idx,
|
||||
psd: self,
|
||||
}));
|
||||
} else if layer_type == 3 {
|
||||
layers.push(Layer::Group(GroupLayer::create(&mut layer_idx, self)?));
|
||||
} else {
|
||||
anyhow::bail!("Unknown layer section divider type: {}", layer_type);
|
||||
}
|
||||
layer_idx += 1;
|
||||
}
|
||||
Ok(layers)
|
||||
}
|
||||
|
||||
/// Returns the normal layers in the PSD file. This function ignores layer groups.
|
||||
pub fn read_normal_layers<'a>(&'a self) -> Result<Vec<NormalLayer<'a>>> {
|
||||
let mut layers = Vec::new();
|
||||
let count = self.psd.layer_count();
|
||||
for layer_idx in 0..count {
|
||||
let layer = &self.psd.layer_and_mask_info.layer_info.layer_records[layer_idx];
|
||||
let layer_type = if let Some(lsct) = layer.get_info(SECTION_DIVIDER_SETTING_KEY) {
|
||||
let type_info = SectionDividerSetting::unpack(
|
||||
&mut MemReaderRef::new(lsct),
|
||||
true,
|
||||
self.encoding,
|
||||
&None,
|
||||
)?;
|
||||
type_info.typ
|
||||
} else {
|
||||
0
|
||||
};
|
||||
if layer_type == 0 {
|
||||
layers.push(NormalLayer {
|
||||
layer,
|
||||
layer_idx,
|
||||
psd: self,
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(layers)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::ext::io::*;
|
||||
use crate::types::*;
|
||||
use crate::utils::encoding::*;
|
||||
use crate::utils::struct_pack::*;
|
||||
use anyhow::Result;
|
||||
use msg_tool_macro::*;
|
||||
@@ -10,6 +11,8 @@ pub const PSD_SIGNATURE: &[u8; 4] = b"8BPS";
|
||||
pub const IMAGE_RESOURCE_SIGNATURE: &[u8; 4] = b"8BIM";
|
||||
pub const LAYER_NAME_SOURCE_SETTING_KEY: &[u8; 4] = b"lnsr";
|
||||
pub const LAYER_ID_KEY: &[u8; 4] = b"lyid";
|
||||
pub const SECTION_DIVIDER_SETTING_KEY: &[u8; 4] = b"lsct";
|
||||
pub const UNICODE_LAYER_KEY: &[u8; 4] = b"luni";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnicodeString(pub String);
|
||||
@@ -176,6 +179,11 @@ impl StructUnpack for ImageResourceSection {
|
||||
while stream_region.cur_pos() < length as u64 {
|
||||
let resource = ImageResourceBlock::unpack(&mut stream_region, big, encoding, info)?;
|
||||
resources.push(resource);
|
||||
if let Ok(d) = stream_region.peek_u8() {
|
||||
if d == 0 {
|
||||
stream_region.read_u8()?; // padding byte
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ImageResourceSection { resources })
|
||||
}
|
||||
@@ -192,6 +200,7 @@ impl StructPack for ImageResourceSection {
|
||||
let mut mem = MemWriter::new();
|
||||
for resource in &self.resources {
|
||||
resource.pack(&mut mem, big, encoding, info)?;
|
||||
// #TODO: check if padding byte is needed
|
||||
}
|
||||
let data = mem.into_inner();
|
||||
let length = data.len() as u32;
|
||||
@@ -213,7 +222,7 @@ pub struct ImageResourceBlock {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LayerAndMaskInfo {
|
||||
pub layer_info: LayerInfo,
|
||||
pub global_layer_mask_info: GlobalLayerMaskInfo,
|
||||
pub global_layer_mask_info: Option<GlobalLayerMaskInfo>,
|
||||
pub tagged_blocks: Vec<u8>,
|
||||
}
|
||||
|
||||
@@ -227,8 +236,18 @@ impl StructUnpack for LayerAndMaskInfo {
|
||||
let length = u32::unpack(reader, big, encoding, info)?;
|
||||
let mut stream_region = StreamRegion::with_size(reader, length as u64)?;
|
||||
let layer_info = LayerInfo::unpack(&mut stream_region, big, encoding, info)?;
|
||||
let global_layer_mask_info =
|
||||
GlobalLayerMaskInfo::unpack(&mut stream_region, big, encoding, info)?;
|
||||
let length = u32::unpack(&mut stream_region, big, encoding, info)?;
|
||||
let global_layer_mask_info = if length > 0 {
|
||||
stream_region.seek_relative(-4)?;
|
||||
Some(GlobalLayerMaskInfo::unpack(
|
||||
&mut stream_region,
|
||||
big,
|
||||
encoding,
|
||||
info,
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut tagged_blocks = Vec::new();
|
||||
stream_region.read_to_end(&mut tagged_blocks)?;
|
||||
Ok(LayerAndMaskInfo {
|
||||
@@ -249,8 +268,11 @@ impl StructPack for LayerAndMaskInfo {
|
||||
) -> Result<()> {
|
||||
let mut mem = MemWriter::new();
|
||||
self.layer_info.pack(&mut mem, big, encoding, info)?;
|
||||
self.global_layer_mask_info
|
||||
.pack(&mut mem, big, encoding, info)?;
|
||||
if let Some(global_layer_mask_info) = &self.global_layer_mask_info {
|
||||
global_layer_mask_info.pack(&mut mem, big, encoding, info)?;
|
||||
} else {
|
||||
0u32.pack(&mut mem, big, encoding, info)?; // no global layer mask info
|
||||
}
|
||||
mem.write_all(&self.tagged_blocks)?;
|
||||
let data = mem.into_inner();
|
||||
let length = data.len() as u32;
|
||||
@@ -262,7 +284,7 @@ impl StructPack for LayerAndMaskInfo {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LayerInfo {
|
||||
pub layer_count: u16,
|
||||
pub layer_count: i16,
|
||||
pub layer_records: Vec<LayerRecord>,
|
||||
pub channel_image_data: Vec<ChannelImageData>,
|
||||
}
|
||||
@@ -276,21 +298,22 @@ impl StructUnpack for LayerInfo {
|
||||
) -> Result<Self> {
|
||||
let length = u32::unpack(reader, big, encoding, info)?;
|
||||
let mut stream_region = StreamRegion::with_size(reader, length as u64)?;
|
||||
let layer_count = u16::unpack(&mut stream_region, big, encoding, info)?;
|
||||
let layer_count = i16::unpack(&mut stream_region, big, encoding, info)?;
|
||||
let mut layer_records = Vec::new();
|
||||
for _ in 0..layer_count {
|
||||
for _ in 0..layer_count.abs() {
|
||||
let layer_record = LayerRecord::unpack(&mut stream_region, big, encoding, info)?;
|
||||
layer_records.push(layer_record);
|
||||
}
|
||||
let mut channel_image_data = Vec::new();
|
||||
for i in 0..layer_count {
|
||||
for i in 0..layer_count.abs() {
|
||||
let layer = &layer_records[i as usize];
|
||||
let info = Some(Box::new(layer.clone()) as Box<dyn Any>);
|
||||
for _ in 0..layer.base.channels {
|
||||
for j in 0..layer.base.channels {
|
||||
let info = Some(Box::new((layer.clone(), j as usize)) as Box<dyn Any>);
|
||||
let data = ChannelImageData::unpack(&mut stream_region, big, encoding, &info)?;
|
||||
channel_image_data.push(data);
|
||||
}
|
||||
}
|
||||
stream_region.seek_to_end()?;
|
||||
Ok(LayerInfo {
|
||||
layer_count,
|
||||
layer_records,
|
||||
@@ -366,6 +389,27 @@ pub struct LayerRecord {
|
||||
pub infos: Vec<AdditionalLayerInfo>,
|
||||
}
|
||||
|
||||
impl LayerRecord {
|
||||
pub fn get_info<'a>(&'a self, key: &[u8; 4]) -> Option<&'a [u8]> {
|
||||
for info in &self.infos {
|
||||
if &info.key == key {
|
||||
return Some(&info.data);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn layer_name(&self, encoding: Encoding) -> Result<String> {
|
||||
if let Some(uni) = self.get_info(UNICODE_LAYER_KEY) {
|
||||
let data = UnicodeLayer::unpack(&mut MemReaderRef::new(uni), true, encoding, &None)?;
|
||||
Ok(data.name.0)
|
||||
} else {
|
||||
let s = decode_to_string(encoding, &self.layer_name.0, true)?;
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StructPack for LayerRecord {
|
||||
fn pack<W: Write>(
|
||||
&self,
|
||||
@@ -548,6 +592,7 @@ impl StructUnpack for LayerMask {
|
||||
let mask_left = i32::unpack(&mut stream_region, big, encoding, info)?;
|
||||
let mask_bottom = i32::unpack(&mut stream_region, big, encoding, info)?;
|
||||
let mask_right = i32::unpack(&mut stream_region, big, encoding, info)?;
|
||||
stream_region.seek_to_end()?;
|
||||
Ok(LayerMask {
|
||||
top,
|
||||
left,
|
||||
@@ -565,6 +610,7 @@ impl StructUnpack for LayerMask {
|
||||
mask_right: Some(mask_right),
|
||||
})
|
||||
} else {
|
||||
stream_region.seek_to_end()?;
|
||||
Ok(LayerMask {
|
||||
top,
|
||||
left,
|
||||
@@ -717,6 +763,17 @@ fn get_layer_info(info: &Option<Box<dyn Any>>) -> Result<&LayerRecord> {
|
||||
))
|
||||
}
|
||||
|
||||
fn get_layer_info_with_channel_index(info: &Option<Box<dyn Any>>) -> Result<&(LayerRecord, usize)> {
|
||||
if let Some(boxed) = info {
|
||||
if let Some(layer_info) = boxed.downcast_ref::<(LayerRecord, usize)>() {
|
||||
return Ok(layer_info);
|
||||
}
|
||||
}
|
||||
Err(anyhow::anyhow!(
|
||||
"LayerRecord and channel index info is required for ChannelImageData unpacking"
|
||||
))
|
||||
}
|
||||
|
||||
impl StructUnpack for ChannelImageData {
|
||||
fn unpack<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
@@ -724,16 +781,17 @@ impl StructUnpack for ChannelImageData {
|
||||
encoding: Encoding,
|
||||
info: &Option<Box<dyn Any>>,
|
||||
) -> Result<Self> {
|
||||
let compression = u16::unpack(reader, big, encoding, info)?;
|
||||
if compression != 0 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Only raw (compression == 0) channel image data is supported"
|
||||
));
|
||||
}
|
||||
let info = get_layer_info(info)?;
|
||||
let len = (info.base.bottom - info.base.top) as usize
|
||||
* (info.base.right - info.base.left) as usize;
|
||||
let image_data = reader.read_exact_vec(len)?;
|
||||
let (layer, idx) = get_layer_info_with_channel_index(info)?;
|
||||
let layer_len = layer
|
||||
.base
|
||||
.channel_infos
|
||||
.get(*idx)
|
||||
.ok_or_else(|| anyhow::anyhow!("Channel index {} out of bounds for layer", idx))?
|
||||
.length;
|
||||
let mut stream_region = StreamRegion::with_size(reader, layer_len as u64)?;
|
||||
let compression = u16::unpack(&mut stream_region, big, encoding, info)?;
|
||||
let mut image_data = Vec::new();
|
||||
stream_region.read_to_end(&mut image_data)?;
|
||||
Ok(ChannelImageData {
|
||||
compression,
|
||||
image_data,
|
||||
@@ -782,6 +840,11 @@ impl StructUnpack for GlobalLayerMaskInfo {
|
||||
info: &Option<Box<dyn Any>>,
|
||||
) -> Result<Self> {
|
||||
let length = u32::unpack(reader, big, encoding, info)?;
|
||||
println!(
|
||||
"GlobalLayerMaskInfo length = {}, stream position = {}",
|
||||
length,
|
||||
reader.stream_position()?
|
||||
);
|
||||
let mut stream_region = StreamRegion::with_size(reader, length as u64)?;
|
||||
let overlays_color_space = u16::unpack(&mut stream_region, big, encoding, info)?;
|
||||
let mut overlays_color_components = [0u16; 4];
|
||||
@@ -793,6 +856,7 @@ impl StructUnpack for GlobalLayerMaskInfo {
|
||||
let filler_length = length as usize - 2 - (4 * 2) - 2 - 1;
|
||||
let mut filler = vec![0u8; filler_length];
|
||||
stream_region.read_exact(&mut filler)?;
|
||||
stream_region.seek_to_end()?;
|
||||
Ok(GlobalLayerMaskInfo {
|
||||
overlays_color_space,
|
||||
overlays_color_components,
|
||||
@@ -828,6 +892,7 @@ impl StructPack for GlobalLayerMaskInfo {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImageDataSection {
|
||||
pub compression: u16,
|
||||
pub image_data: Vec<u8>,
|
||||
@@ -850,18 +915,8 @@ impl StructUnpack for ImageDataSection {
|
||||
info: &Option<Box<dyn Any>>,
|
||||
) -> Result<Self> {
|
||||
let compression = u16::unpack(reader, big, encoding, info)?;
|
||||
if compression != 0 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Only raw (compression == 0) image data is supported"
|
||||
));
|
||||
}
|
||||
let psd_header = get_psd_header(info)?;
|
||||
let len = psd_header.channels as usize
|
||||
* psd_header.height as usize
|
||||
* psd_header.width as usize
|
||||
* psd_header.depth as usize
|
||||
/ 8;
|
||||
let image_data = reader.read_exact_vec(len)?;
|
||||
let mut image_data = Vec::new();
|
||||
reader.read_to_end(&mut image_data)?;
|
||||
Ok(ImageDataSection {
|
||||
compression,
|
||||
image_data,
|
||||
@@ -896,6 +951,7 @@ impl StructPack for ImageDataSection {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PsdFile {
|
||||
pub header: PsdHeader,
|
||||
pub color_mode_data: ColorModeData,
|
||||
@@ -924,6 +980,35 @@ impl StructPack for PsdFile {
|
||||
}
|
||||
}
|
||||
|
||||
impl StructUnpack for PsdFile {
|
||||
fn unpack<R: Read + Seek>(
|
||||
reader: &mut R,
|
||||
big: bool,
|
||||
encoding: Encoding,
|
||||
info: &Option<Box<dyn Any>>,
|
||||
) -> Result<Self> {
|
||||
let header = PsdHeader::unpack(reader, big, encoding, info)?;
|
||||
let psd_info = Some(Box::new(header.clone()) as Box<dyn Any>);
|
||||
let color_mode_data = ColorModeData::unpack(reader, big, encoding, &psd_info)?;
|
||||
let image_resource = ImageResourceSection::unpack(reader, big, encoding, &psd_info)?;
|
||||
let layer_and_mask_info = LayerAndMaskInfo::unpack(reader, big, encoding, &psd_info)?;
|
||||
let image_data = ImageDataSection::unpack(reader, big, encoding, &psd_info)?;
|
||||
Ok(PsdFile {
|
||||
header,
|
||||
color_mode_data,
|
||||
image_resource,
|
||||
layer_and_mask_info,
|
||||
image_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PsdFile {
|
||||
pub fn layer_count(&self) -> usize {
|
||||
self.layer_and_mask_info.layer_info.layer_count.abs() as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, StructPack, StructUnpack)]
|
||||
pub struct SectionDividerSetting {
|
||||
pub typ: u32,
|
||||
@@ -938,7 +1023,7 @@ pub struct UnicodeLayer {
|
||||
#[derive(Debug, Clone, StructPack, StructUnpack)]
|
||||
pub struct LayerID {
|
||||
/// ID for the layer
|
||||
pub id: i32,
|
||||
pub id: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, StructPack, StructUnpack)]
|
||||
|
||||
Reference in New Issue
Block a user