实现了 ChunkType
和 Chunk
结构体之后,接下来实现一个 Png
结构体,用于表示和操作 PNG 文件。它包含了 PNG 文件的标准头和一系列数据块(Chunk
),并提供了从字节数据创建 Png
实例、添加和移除数据块、查找特定类型数据块以及将 Png
实例转换为字节数据等功能。
rust
use std::convert::TryFrom;
use std::fmt;
use anyhow::{Result, Error};
use std::str::FromStr;
use crate::chunk::Chunk;
use crate::chunk_type::ChunkType;
导入必要的模块和类型。std::convert::TryFrom
用于实现从字节切片转换为 Png
实例;std::fmt
用于实现 Display
trait,以便格式化输出 Png
实例;anyhow
提供了统一的错误处理机制;std::str::FromStr
用于将字符串转换为特定类型;crate::chunk::Chunk
和 crate::chunk_type::ChunkType
分别表示 PNG 文件的数据块和数据块类型。
Png
结构体定义
rust
pub struct Png {
header: [u8; 8],
chunks: Vec<Chunk>,
}
Png
结构体包含两个字段:header
是一个长度为 8 的字节数组,用于存储 PNG 文件的标准头;chunks
是一个 Chunk
类型的向量,用于存储 PNG 文件的所有数据块。
常量定义与构造函数
rust
impl Png {
// 定义标准的 PNG 文件头常量
pub const STANDARD_HEADER: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];
// 从数据块向量创建 Png 实例
pub fn from_chunks(chunks: Vec<Chunk>) -> Png {
Png {
header: Self::STANDARD_HEADER,
chunks,
}
}
// 向 Png 实例中追加一个数据块
pub fn append_chunk(&mut self, chunk: Chunk) {
self.chunks.push(chunk);
}
// 移除第一个指定类型的数据块
pub fn remove_first_chunk(&mut self, chunk_type: &str) -> Result<Chunk> {
let chunk_type = ChunkType::from_str(chunk_type)?;
if let Some(index) = self.chunks.iter().position(|c| *c.chunk_type() == chunk_type) {
Ok(self.chunks.remove(index))
} else {
Err(Error::msg(format!("Chunk of type {} not found", chunk_type)))
}
}
// 返回 PNG 文件头的引用
pub fn header(&self) -> &[u8; 8] {
&self.header
}
// 返回所有数据块的切片
pub fn chunks(&self) -> &[Chunk] {
&self.chunks
}
// 根据数据块类型查找数据块
pub fn chunk_by_type(&self, chunk_type: &str) -> Option<&Chunk> {
let chunk_type = match ChunkType::from_str(chunk_type) {
Ok(ct) => ct,
Err(_) => return None,
};
self.chunks.iter().find(|c| *c.chunk_type() == chunk_type)
}
// 将整个 PNG 文件转换为字节向量
pub fn as_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::from(self.header);
for chunk in &self.chunks {
bytes.extend_from_slice(&chunk.as_bytes());
}
bytes
}
}
从字节切片转换为 Png
实例
rust
// 实现从字节切片转换为 Png 实例
impl TryFrom<&[u8]> for Png {
type Error = Error;
fn try_from(value: &[u8]) -> Result<Self> {
if value.len() < 8 || &value[0..8] != &Self::STANDARD_HEADER {
return Err(Error::msg("Invalid PNG header"));
}
let mut chunks = Vec::new();
let mut index = 8;
while index < value.len() {
let chunk = Chunk::try_from(&value[index..])?;
chunks.push(chunk.clone());
index += 4 + 4 + chunk.length() as usize + 4;
}
Ok(Png {
header: Self::STANDARD_HEADER,
chunks,
})
}
}
格式化输出
rust
impl fmt::Display for Png {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "PNG File:")?;
writeln!(f, " Header: {:?}", self.header)?;
writeln!(f, " Chunks:")?;
for chunk in &self.chunks {
writeln!(f, " {}", chunk)?;
}
Ok(())
}
}
以上就是通过定义 Png
结构体和相关方法,提供了一个完整的 PNG 文件解析和操作方案。