写一个 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 文件解析和操作方案。

相关推荐
学渣6765621 小时前
文件传输工具rsync|rust开发环境安装|Ascend实验相关命令
开发语言·后端·rust
咸甜适中1 天前
rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(二十四)窗口颜色、透明度、居中显示
笔记·学习·rust·egui
或与且与或非1 天前
Rust+slint实现一个登录demo
开发语言·rust·状态模式
BoredWait1 天前
rust 嵌入式esp23 《直流电机》
rust·嵌入式
l1t2 天前
DuckDB新版rusty_sheet 插件测试
xml·数据库·rust·插件·xlsx·duckdb
万添裁2 天前
移动语义:从C++到rust
c++·rust·移动语义
EthanChou20202 天前
rust学习之开发环境
开发语言·学习·rust
Source.Liu3 天前
【学Python自动化】 8.1 Python 与 Rust 错误处理对比学习笔记
python·rust·自动化
夕水3 天前
一起来学习rust3-变量与可变性
rust
ftpeak3 天前
Rust SQLx 开发指南:利用 Tokio 进行性能优化
开发语言·oracle·性能优化·rust·个人开发