概述
首先,关于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),可知图片的宽高为512x512。其后的5个字节分别表示了图像的色深、颜色类型、滤波器方法、隔行扫描方法,最后四个字节为CRC循环冗余检测。” ①“
IHDR由13字节组成:
名称 字节数 说明 Width 4字节 图像宽度(像素) Height 4字节 图像高度(像素) Bit depth 1字节 图像深度:表示每个采样点占用的bit数
索引(indexed)彩色图像:1,2,4或8;
灰度图像:1,2,4,8或16;
真彩色图像:8或16Colour type 1字节 颜色类型:
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 bitCompression method 1字节 压缩方法:
0:LZ77派生算法(目前仅定义了0)
其他值:无效;为未来扩展的压缩方法预留Filter method 1字节 滤波器方法:
0(目前仅定义了0)
其他值:无效Interlace method 1字节 隔行扫描方法:
0:非隔行扫描;
1: Adam7隔行扫描方法”②
作为初学者,我个人的想法是创建一个图片结构体,将整张图片以byte[]形式存储,然后设置偏移值跳过不需要的信息,然后使用联合将需要转换的信息转换为相应的数据类型并储存即可。
联合部分:使用联合将4个byte与1个int共用一片存储空间,反向读取整个图片的byte[]数组,对于4字节长的width与height信息,正向存储于联合的byte[]数组,达到byte[]转int的效果。
代码
/* * 处理PNG图片的c文件 * */ #include <stdio.h> #include <windows.h> //定义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转int byteToInt 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; }