写一个 Rust 小应用 pngme - 3.实现PNG

实现了 ChunkTypeChunk 结构体之后,接下来实现一个 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::Chunkcrate::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 文件解析和操作方案。

相关推荐
Yeauty7 小时前
Rust 中的高效视频处理:利用硬件加速应对高分辨率视频
开发语言·rust·ffmpeg·音视频·音频·视频
zhu12893035568 小时前
基于Rust与WebAssembly实现高性能前端计算
前端·rust·wasm
关山月9 小时前
Rust 如何处理闭包:Fn、FnMut 和 FnOnce
rust
Vitalia16 小时前
从零开始学Rust:枚举(enum)与模式匹配核心机制
开发语言·后端·rust
一只小松许️18 小时前
Rust闭包详解
开发语言·rust
SoFlu软件机器人21 小时前
Go/Rust 疯狂蚕食 Java 市场?老牌语言的 AI 化自救之路
java·golang·rust
小白学大数据1 天前
异步读取HTTP响应体的Rust实现
网络协议·http·rust
Source.Liu1 天前
【学Rust写CAD】24 扫描渐变(sweep_gradient.rs)
后端·rust
一只小松许️1 天前
Rust迭代器详解
rust
机构师1 天前
<tauri><rust><GUI>基于rust和tauri,实现一个svg转png的工具
javascript·后端·rust