首页 文章详情

极简的 PNG 编码函数 svpng(),用来学习C语言,真的很爽

嵌入式Linux | 436 2021-07-09 20:31 0 0 0
UniSMS (合一短信)

这个是在知乎上看到的大神写的文章,如果是学习C语言入门的,我觉得可以从这个入手,特别是对图像感兴趣的。


文章中提到的「我」,指的是「Milo Yip」大神。


1.    什么是png格式图片?


相对地,PNG(Portable Network Graphics)就是一个广为人知的图片格式。如果可以把影像直接储存成 PNG,不是更理想么?

然而,在 C/C++ 中写入 PNG 一般需要链接一些程序库,例如 PNG 的标准参考程序库是 libpng。它很强大,支持 PNG 所有功能,但对于初学者而言,配置、编译并学习如何使用这些程序库,可能已足够打消动手的念头。

可以简单一点么?

2.    svpng

为此,我在周末尝试写一个极简的 C 函数 Github miloyip/svpng(save PNG 的缩写),它仅能写入 24-bit RGB 或 32-bit RGBA、无压缩的 PNG。它只有一个 32 行代码的函数。

github地址:

https://github.com/miloyip/svpng


用法如下:

#include "svpng.inc"

void test_rgb(void) {
    unsigned char rgb[256 * 256 * 3], *p = rgb;
    unsigned x, y;
    FILE *fp = fopen("rgb.png""wb");
    for (y = 0; y < 256; y++)
        for (x = 0; x < 256; x++) {
            *p++ = (unsigned char)x;    /* R */
            *p++ = (unsigned char)y;    /* G */
            *p++ = 128;                 /* B */
        }
    svpng(fp, 256256, rgb, 0);
    fclose(fp);
}


就会输出这个 rgb.png 文件:

这个函数的声明很简单,缺省配置下是这样的:

/*!
    \brief 以 PNG 格式存储 RGB/RGBA 影像
    \param out 输出流(缺省使用 FILE*)。
    \param w 影像宽度。(<16383)
    \param h 影像高度。
    \param img 影像像素数据,内容为 24 位 RGB 或 32 位 ARGB 格式。
    \param alpha 影像是否含有 alpha 通道。
*/

void svpng(FILE* out, unsigned w, unsigned h, const unsigned char* img, int alpha);


相信这样的函数时使对初学者而言,也极易使用。也不需要另外生成程序库,只要复制到项目便可使用。


3.    代码实现

这里简单介绍实现要点,对此没兴趣的读者也可略过。

根据 Portable Network Graphics (PNG) Specification (Second Edition) 「www.w3.org」,PNG 文件由多个 chunk 组成。每个 chunk 的类型以 4 个字符表示。最基本的 PNG 文件内容是:

  • 8 字节 magic number:用于识别 PNG 格式

  • IHDR(Image Header) chunk:描述影像的维度、色彩深度、色彩格式、压缩类型等

  • IDAT(Image Data)chunk:存储影像的像素数据

  • IEND(Image End)chunk:PNG数据流结束

每个 chunk 的结构是:

  • chunk 内容长度(4 字节)

  • chunk 类型(4 字节)

  • chunk 内容

  • chunk 的 CRC(包括类型和内容)

PNG 里的数据是以大端(big endian)编码的,但在 IDAT 中,每个 block 的长度则以小端存储。另外,实现的难点之一,是要同时实现 CRC-32 及 Adler-32 校验和(checksum)的生成。

编码实现如下(文字版请移玉步至 svpng.inc):

为了减少代码大小,使用宏去避免加入多个函数。另外,为了简化实现,把每一行像素写成一个 block,这样可能会浪费一点空间,但对于这函数而言也不是问题。


4.     结语

本文介绍了一个极简的 C 函数 svpng,方便在 C/C++ 中把图像存储成 PNG 文件,并简介了当中的实现。希望读者能利用此函数,进入计算机图形学之门。

===

文中的 inc 而不是.h,是因为inc 是对方法的实现,而不是简单的声明。

unsigned char rgb[256 * 256 * 3] ,可能有的人不明白为什么数组要这样声明,一个像素点是通过{R,G,B}三色值表示的,所以后面有一个3,但是一张图片,是包含有长和宽的,也就是前面的256*256,就是这张图片的长和宽。实际上的图片是这样排列的 { R, G, B, R, G, B, ... } 的形式,从上至下,左至右。





#推荐阅读#


专辑|Linux文章汇总
专辑|程序人生
专辑|C语言
我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

嵌入式Linux

微信扫描二维码,关注我的公众号



good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter