Rust编程入门 阅读代码 - 一个项目例子:解析BMP位图文件
前言
不是本人项目,只是 Github 看到的,很适合入门学习,所以写下这篇文章
- 当前commit版本:https://github.com/sondrele/rust-bmp/commit/0425dadd43de63f9f89030780f2f3b9fee7e868c
- 在线查看代码:https://github.dev/sondrele/rust-bmp/blob/master/src/decoder.rs
- BMP 文件的帧数据格式:https://zh.wikipedia.org/wiki/BMP#位图文件头
BMP 格式帧
BMP取自位图Bitmap的缩写,也称为DIB(与设备无关的位图),是一种独立于显示器的位图数字图像文件格式。常见于微软视窗和OS/2操作系统,Windows GDI API内部使用的DIB数据结构与 BMP 文件格式几乎相同。
图像通常保存的颜色深度有2(1位)、16(4位)、256(8位)、65536(16位)和1670万(24位)种颜色(其中位是表示每点所用的数据位)。8位图像可以是索引彩色图像外,也可以是灰阶图像。表示透明的alpha通道也可以保存在一个类似于灰阶图像的独立文件中。带有集成的alpha通道的32位版本已经随着Windows XP出现,它在视窗的登录和主题系统中都有使用。
感性理解一下
开始阅读代码(解码部分)
源码在 src 目录里,测试例子在 example 目录。
我们要先看 解析bmp格式文件,看一下文件列表,那么肯定是 decoder.rs
解析-位图文件头
WIKI: 这部分数据块位于文件开头,用于进行文件的识别。典型的应用程序会首先普通读取这部分数据以确保的确是位图文件并且没有损坏。所有的整数值都以小端序存放(即最低有效位前置)。
注:1字节 = 8Bits
L87 调用 read_bmp_id(bmp_data)?; 读取魔数
这个魔数用于确定这是一个BMP文件
这里只适配了最常见的 BM
L88 调用 let header = read_bmp_header(bmp_data)?; 读取剩下文件头的部分
注:这里的 bmp_data是Cursor<Vec<u8>>
类型的,适配着BMP格式的 8bits 小端流。 read_u32之类的是通过 https://docs.rs/byteorder/latest/byteorder/trait.ReadBytesExt.html 实现的
解析-DIB头
L89 调用 let dib_header = read_bmp_dib_header(bmp_data)?;
匹配 Bmp版本
其中 BmpVersion::from_dib_header 的实现如下
对应着数据帧里的版本格式
匹配 像素格式(每像素有多少个比特
像素信息是在前面的Dib头解析过的,在这里只是做一下判断处理
匹配 压缩类型
其中 CompressionType 源码如下
对应着帧数据格式的
最后返回dib结果
附加位掩码
解析 调色板
L91 解析 let color_palette = read_color_palette(bmp_data, &dib_header)?;
解析像素
解析出像素数组并用于调用 fn read_indexes(
对应着
其中用于读取索引的函数 fn read_indexes(
源码如下
用于读取像素数据的函数 fn read_pixels(
源码如下
最后构造出整个BMP文件数据帧的结构体/内存形态
对应开头的 BMP 格式帧
注:填充区就是用于保持内存/结构体对齐的,即填充一些无用数据,保持内存对齐,这样不会降低硬件的读取速度(硬件一般都需要数据对齐才能保持最佳的速度
这个项目采用通用解法,即解析时根据偏移跳过填充区,因为读取填充区没有意义,我们只需要读取的时候能对齐就行。
阅读代码(编码部分)
比较清晰了然,直接上图
其中 Image 结构体如下
写入到文件
项目总结
-
src 目录
-
- decoder.rs 和 encoder.rs 是解码和编码的
-
- consts.rs 是颜色集
- consts.rs 是颜色集
-
- lib.rs 里面,还有一堆测试代码
- lib.rs 里面,还有一堆测试代码
-
test 目录里是测试资源,例如 lib.rs 里的测试代码就会读取这里的测试图片
-
example 目录里是一个 main 函数例子