概述

首先,关于png图像的结构:PNG文件的结构、PNG格式的数据结构。这两篇文章说的比较细。我简单地说一下我使用到的地方:

注:①引于PNG格式的数据结构。②引于PNG文件的结构

“png文件的前8个字节为固定的文件头信息,表明为png文件,其后便为IHDR。
IHDR的前1-4字节表示IHDR的长度(00 00 00 0D),可知长度为13。5-8字节(49 48 44 52)为数据块类型码,表明数据块为IHDR。9-16字节存储了图像的宽高信息(00 00 20 00 00 00 20 00),可知图片的宽高为512×512。其后的5个字节分别表示了图像的色深、颜色类型、滤波器方法、隔行扫描方法,最后四个字节为CRC循环冗余检测。” ①

IHDR由13字节组成:

名称字节数说明
Width4字节图像宽度(像素)
Height4字节图像高度(像素)
Bit depth1字节图像深度:表示每个采样点占用的bit数
索引(indexed)彩色图像:1,2,4或8;
灰度图像:1,2,4,8或16;
真彩色图像:8或16
Colour type1字节颜色类型:
0:灰度图像, 深度为1,2,4,8或16 bit;
2:真彩色(truecolor)图像,深度为8或16 bit;
3:索引彩色图像,深度为1,2,4或8 bit;
4:带α通道数据的灰度图像,深度为8或16 bit;
6:带α通道数据的真彩色(truecolor with alpha)图像,深度为8或16 bit
Compression method1字节压缩方法:
0:LZ77派生算法(目前仅定义了0)
其他值:无效;为未来扩展的压缩方法预留
Filter method1字节滤波器方法:
0(目前仅定义了0)
其他值:无效
Interlace method1字节隔行扫描方法:
0:非隔行扫描;
1: Adam7隔行扫描方法

”②

作为初学者,我个人的想法是创建一个图片结构体,将整张图片以byte[]形式存储,然后设置偏移值跳过不需要的信息,然后使用联合将需要转换的信息转换为相应的数据类型并储存即可。

联合部分:使用联合将4个byte与1个int共用一片存储空间,反向读取整个图片的byte[]数组,对于4字节长的width与height信息,正向存储于联合的byte[]数组,达到byte[]转int的效果。

代码

/* * 处理PNG图片的c文件 * */#include #include //定义PNG图片体typedef struct pictureTypedPNG{//存储图片路径char* path;//图像宽度、图像高度int width, height;//图片大小long long pictureSize;//图像深度、颜色类型、压缩方法、滤波器方法、隔行扫描方法byte depth, colorType, compressionMethod, filterMethod, interlaceMethod;//图像本体存储在这里byte* body;}png;//定义byte转int的联合体typedef union byteToInt{__attribute__((unused)) byte b[4];//只做转换用,不直接调用int i;}byteToInt;//图片读取png* getPNG(char* path){png* p = (png*)malloc(sizeof(png));FILE *fp = fopen(path, "rb");//打开文件。if (fp == NULL) // 打开文件失败return p;//存储路径p->path = (char*) malloc((sizeof(char) * strlen(path)));strcpy(p->path, path);//获取文件大小fseek(fp, 0, SEEK_END);//定位文件指针到文件尾。p->pictureSize = ftell(fp);//获取文件指针偏移量,即文件大小。fseek(fp, 0, SEEK_SET);//定位文件指针到文件头。//获取图片体p->body = (byte*) malloc(sizeof(byte) * p->pictureSize);//分配存储图片文件的内存fread(p->body, 1, p->pictureSize, fp);//读取图片体//多byte转intbyteToInt bti;//获取图片部分信息。设置偏移值滤除文件头、IHDR标识信息int offset = 8 + 8;//获取图像宽度for(int i = 3, j = 0; i >= 0; i--, j++){bti.b[i] = p->body[j + offset];}p->width = bti.i;//获取图像高度for(int i = 3, j = 0; i >= 0; i--, j++){bti.b[i] = p->body[j + offset + 4];}p->height = bti.i;//获取图像深度p->depth = p->body[8 + offset];//获取颜色类型p->colorType = p->body[9 + offset];//获取压缩方法p->compressionMethod = p->body[10 + offset];//获取滤波器方法p->filterMethod = p->body[11 + offset];//获取隔行扫描方法p->interlaceMethod = p->body[12 + offset];fclose(fp);//关闭文件。return p;}void printPNG(const png* p){const char* colorType = NULL;const char* compressionMethod = NULL;const char* filterMethod = NULL;const char* interlaceMethod = NULL;switch(p->colorType){case 0: colorType = "灰度图像";break;case 2: colorType = "真彩色图像";break;case 3: colorType = "索引彩色图像";break;case 4: colorType = "带α通道的灰度图像";break;case 6: colorType = "带α通道的真彩色图像";break;}if(!p->compressionMethod){compressionMethod = "LZ77派生算法";}if(!p->filterMethod){filterMethod = "0";}if(p->interlaceMethod){interlaceMethod = "Adam7隔行扫描方法";}else{interlaceMethod = "非隔行扫描";}printf("图像路径:%s\n" "图像大小:%lld\n" "图像宽度(像素):%d\n" "图像高度(像素):%d\n" "图像深度:%hhu\n" "颜色类型:%s\n" "压缩方法:%s\n" "滤波器方法:%s\n" "隔行扫描方法:%s\n", p->path, p->pictureSize, p->width, p->height, p->depth, colorType, compressionMethod, filterMethod, interlaceMethod);}int main() {printf("请输入图片路径: \n");char* path = (char*)malloc(sizeof(char) * 100);fgets(path, 100, stdin);int len = (int)strlen(path);*(path + len - 1) = '\0';png* picture = getPNG(path);printPNG(picture);return 0;}

实现效果