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

相关推荐
heroboyluck1 小时前
rust 全栈应用框架dioxus server
rust·全栈·dioxus
蜗牛沐雨4 小时前
Rust 中的 `PartialEq` 和 `Eq`:深入解析与应用
开发语言·后端·rust
Python私教4 小时前
Rust快速入门:从零到实战指南
开发语言·后端·rust
明月看潮生6 小时前
青少年编程与数学 02-019 Rust 编程基础 10课题、函数、闭包和迭代器
开发语言·青少年编程·rust·编程与数学
明月看潮生6 小时前
青少年编程与数学 02-019 Rust 编程基础 09课题、流程控制
开发语言·算法·青少年编程·rust·编程与数学
一丝晨光12 小时前
数值溢出保护?数值溢出应该是多少?Swift如何让整数计算溢出不抛出异常?类型最大值和最小值?
java·javascript·c++·rust·go·c·swift
景天科技苑12 小时前
【Rust泛型】Rust泛型使用详解与应用场景
开发语言·后端·rust·泛型·rust泛型
zhuziheniaoer13 小时前
rust-candle学习笔记11-实现一个简单的自注意力
笔记·学习·自然语言处理·rust
明月看潮生1 天前
青少年编程与数学 02-019 Rust 编程基础 08课题、字面量、运算符和表达式
开发语言·青少年编程·rust·编程与数学
天天打码1 天前
Rspack:字节跳动自研 Web 构建工具-基于 Rust打造高性能前端工具链
开发语言·前端·javascript·rust·开源