PNG图片CRC校验码的计算过程和总结

/*

问题:一张PNG图片HIDR Chunk中的数据为:

00 00 00 0D 49 48 44 52 00 00 00 DC 00 00 00 DC 10 06 00 00 00 4B CA 13 C2

Chunk Type Code域和Chunk Data部分是:49 48 44 52 00 00 00 DC 00 00 00 DC 10 06 00 00 00

如何验证用该数据计算得到的CRC校验码是:4B CA 13 C2 ?

下面是代码验证过程代码和总结。

方法:用比较法来验证,为了得到结果综合法分析结合转换思想,逐步推进。

*/
# include <stdio.h>
# include <string.h>
/*1.char*,unsigned char*,char,unsigned char的理解
char*,unsigned char*记录的就是二进制转换到的十六进制的字节码[整(整数补码(正整数还是原码负整数补码),浮点数IEEE74原码,字符编码原码),
存放顺序是先入先存放的,超过一个字节就会区分大小端存放。char*,unsigned char*指针转换到char,unsigned char会转换到字符类型。
但是char,unsigned char整数运算还是十六进制的字节码(包括位运算之类),所以可以提供十六进制整数序列,用整数指针访问序列每个元素(对于十六进制的字节码直接进行CRC计算。
如果希望char,unsigned char整数运算,能得到从整数来的值那么需要截取一个字节来处理;或者采用结构体赋予整型值放置在内存或者二进制文件中读取出来,然后一个个char,unsigned char整数运算
处理。

 2.大小端转换,png图片数据是用Big-Endian存储数值数据的需要转换为大端模式。
数值数据存在大小端区别,CC++结构体存储数值数据到二进制文件中,是以小端模式存放的,其实内存中也是小端模式的字节存放的(主流的CPU都这样);而PNG数值类型数据是以
大端模式存放的,所以从二进制文件中读取出来的时候需要经过大端到小端的转换,写入到png文件中的时候需要经过小端到大端的转换,而png的一些基于二进制CRC校验
都是在大端模式下的缓存块中进行的(压缩不确定是否如此,应该是在小端模式下进行压缩编码的)。

 3.结构体内存对齐,结构体在内存中本身存在内存对齐,所以写入到二进制文件中也有内存对齐问题, 读取出来时候需要减掉得到真实有用数据。

 结构体内存对齐参数由#pragma pack(8)决定默认是8
 1)第一个是系统找一个以对齐参数N倍数的地址为初地址(首地址偏移为0),后面的地址依次用min(N,本成员)作为实际对齐参数,该参数首个倍数地址(从首地址偏移)作为成员内存地址;
 2) 内存对齐最终大小,是max(n1,n2,n3,...)最大元素的整数倍,不够后面就补0字节。

 特殊:
 1)嵌套结构体是作为一个递归成员,内部成员大小为n自身对齐min(n,N)。但是内部第一个偏移:将内部最大作为n最终取min(n,N)作为对齐参数,首个倍数地址为内存地址。
内部右边界到外部第一个偏移:用的就是外部的成员作为n最终取min(n,N),首个倍数地址作为内存地址。
 2) 数组成员(拆分):包含数组时候,整个数组不是作为一个成员,而是里面的每个元素作为一个结构体成员。
 3) 有位段时的对齐规则(组合):同类型的、相邻的可连续在一个类型的存储空间中存放的位段成员作为一个该类型的成员变量来对待, 不是同类型的、相邻的位段成员,分别当作一个单独得该类型的成员来对待,分配一个完整的类型空间,其长度为该类型的长度,其他成员的分配规则不变,仍然按照前述的对齐规则进行。
*/

typedef unsigned int uint ;
typedef unsigned char BYTE ;
static uint POLYNOMIAL = 0xEDB88320 ;
uint g_CRCTable[256] ;
// 定义4MB 4194304为最大读取,如果大于也是读取4MB内容作为CRC计算
#define MAXLEN 1024

#define SToB_32(digitalValue) (uint((BYTE)digitalValue) << 24 \
							  | uint((BYTE)digitalValue >> 8) << 16\
							  | uint((BYTE)digitalValue >> 16) << 8\
							  | uint((BYTE)digitalValue >> 24))
void init_table()
{
	int i, j, crc ;
	for (i = 0 ; i < 256 ; i++)
		for (j = 0, g_CRCTable[i] = i ; j < 8 ; j++)
			g_CRCTable[i] = (g_CRCTable[i]>>1)^((g_CRCTable[i]&1)?POLYNOMIAL:0) ;
}


uint crc32(uint crc, char *buff, int len)
{
	crc = ~crc;
	for (int i = 0; i < len; i++)
	{
		int data = buff[i];
		crc = (crc >> 8) ^ g_CRCTable[(crc ^ buff[i]) & 0xff];
	}
	return ~crc;
}

uint crc32_foruint(uint crc, unsigned int data[], int len)
{
	crc = ~crc;
	for (int i = 0; i < len; i++)
	{
		int nData = data[i];
		crc = (crc >> 8) ^ g_CRCTable[(crc ^ data[i]) & 0xff];
	}
	return ~crc;
}

uint crc32_fromFile(const char *pDestPath, int nAlignWidth)
{
	uint crcValue = 0;
	FILE *pFile = NULL;
	pFile = fopen(pDestPath, "rb");
	if(pFile == NULL)
	{
		printf("fopen:%s fail!", pDestPath);
		return 0;
	}
	
	fseek(pFile,0L, SEEK_END);
	int nFileLenght = ftell(pFile) - nAlignWidth; //减去结构体对齐大小
    fseek(pFile,0L, SEEK_SET);
	if(nFileLenght > MAXLEN)
	{
		printf("File: %s 's size is bigger than %u byte.", pDestPath, unsigned int(MAXLEN));
	}
	char buffer[MAXLEN];
	fread(buffer, sizeof(char), nFileLenght, pFile);
	fclose(pFile);

	crcValue = crc32(crcValue, buffer, nFileLenght);
	
	return crcValue;
}

bool writetofile(const char *pDestPath, unsigned char buffer[], int count)
{
	FILE *pFile = NULL;
	pFile = fopen(pDestPath, "wb");
	if(pFile ==  NULL)
	{
		printf("fopen or create:%s fail!", pDestPath);
		return false;
	}
	fwrite(buffer, sizeof(unsigned char), count, pFile);
	fclose(pFile);
	return true;
}

int main ()
{
	// 初始化表
	init_table();

	char s[] = "aaaaaa";
	printf("%08X\n", crc32(0, s, strlen(s)));

	unsigned int crcFileValue = crc32_fromFile("f:\\data\\testcrc.txt", 0);// testcrc.txt里面是aaaaaaa
	printf("%08X\n",crcFileValue);

	// arrayData是PNG图片的IHDR数据块的Chunk Type Code域和Chunk Data域中的数据
	// 含义:0x49,0x48,0x44,0x52是"IHDR"Chunk块标志位,0x00,0x00,0x00,0xDC是PNG的Big-Endian表示的宽220pixel
	// 高也是220pixel,深度是0x10真彩色,颜色类型是0x06带a类型的真彩色,0x00表示用Deflate压缩编码压缩图像数据
	// 0x00表示将来使用更好的压缩方法预留,0x00表示非隔行扫描.
	unsigned int arrayData[17] = {/*0x00,0x00,0x00,0x0D,*/0x49,0x48,0x44,0x52,0x00,0x00,0x00,\
		0xDC,0x00,0x00,0x00,0xDC,0x10,0x06,0x00,0x00,0x00};// 图像中的CRC为 0x4BCA13C2
	unsigned int crcValue = crc32_foruint(0, arrayData, 17);
	printf("%08X\n", crcValue);// 验证输出确实为0x4BCA13C2,也是crcValue的Big-Endian模式的一个输出。

	struct tagFileHead
	{
		unsigned char mark[4];
		unsigned int widthPixel;
		unsigned int heightPixel;
		unsigned char methon[5];
	};
	tagFileHead fileHead;
	fileHead.mark[0] = 'I';
	fileHead.mark[1] = 'H';
	fileHead.mark[2] = 'D';
	fileHead.mark[3] = 'R';
	fileHead.widthPixel = SToB_32(220);
	fileHead.heightPixel = SToB_32(220);
	fileHead.methon[0] = 16;
	fileHead.methon[1] = 6;
	fileHead.methon[2] = 0;
	fileHead.methon[3] = 0;
	fileHead.methon[4] = 0;
	/*fileHead.methon[4] = NULL;
	fileHead.methon[4] = 0;
	fileHead.methon[4] = 0;*/
	const char *pDestPath = "f:\\data\\crc";
	FILE *pFile = fopen(pDestPath, "wb");
	if(pFile ==  NULL)
	{
		printf("fopen or create:%s fail!", pDestPath);
		return false;
	}
	fwrite((void*)&fileHead, sizeof(tagFileHead), 1, pFile);
	fclose(pFile);

	unsigned int crcFileValue2 = crc32_fromFile("f:\\data\\crc", 3);
	printf("%08X\n",crcFileValue2);
	

	return 0 ;
}
;


  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: PNG(Portable Network Graphics)是一种无损压缩的图像格式,它使用CRC(循环冗余校验)算法来生成并验证图像的检验CRC是一种校验算法,用于检测数据传输或存储中的错误。它将数据视为一个多项式,并通过除法运算来计算CRC值。在PNG图像中,CRC用于检验数据块的完整性,以确保图像数据没有损坏或遭到篡改。 PNG图像由多个数据块(Chunks)组成,其中包括图像数据、元数据、调色板等信息。每个数据块都有一个4字节的CRC校验,位于数据块的结尾处。CRC校验由数据块的类型字段和数据字段一起计算得出。 CRC校验计算公式如下: 1. 首先,将数据块的类型字段和数据字段连接起来,形成一个新的数据序列。 2. 初始化一个CRC校验值为0xFFFFFFFF。 3. 遍历数据序列的每个字节,按位异或运算更新CRC校验值。 4. 对于每个字节,进行8次循环,每次循环右移1位并判断最低位是否为1,若是,则与预先定义的多项式0xEDB88320按位异或。 5. 循环结束后,对CRC校验值进行按位取反操作。 6. 最终的CRC校验即为取反后的CRC校验值。 使用CRC校验可以验证图像数据在传输或存储过程中的完整性和正确性。在接收或读取PNG图像时,可以重新计算CRC校验并与原来的校验进行比较,若二者相等,则可以确认图像数据没有损坏或被篡改。这对于保证图像质量和完整性非常重要。 ### 回答2: PNG(Portable Network Graphics)是一种无损压缩的图像文件格式,其中的CRC(Cyclic Redundancy Check)是一种用于验证数据完整性的校验PNG图像文件在存储时,会对图像数据进行压缩和编处理。而CRC检验则是在编过程中生成的一串16位二进制数,用于验证图像数据在传输或存储过程中是否发生了变化或损坏。 CRC检验计算过程如下: 1. 初始化一个16位的寄存器,将其值设置为全1。 2. 将待计算的数据按字节或按位逐个与寄存器的值异或操作。 3. 对于每个被异或的字节(或位),将寄存器向右移动一位,并在最高位上填充0。 4. 若寄存器最右边的位为1,则将寄存器的值与一个预设的常数多项式进行异或操作。 5. 重复步骤2到4,直到所有的数据都被处理完。 6. 最后,将得到的寄存器值作为CRC校验CRC检验的应用可以有效地检测到数据传输过程中的误和损坏。当接收端接收到数据后,同样按照上述步骤计算接收到的数据的CRC值,并将其与发送端传输的CRC值进行比较。若两个值不相等,则说明数据发生了变化或损坏。 通过使用CRC检验,可以提高数据的完整性和可靠性,保证在数据传输和存储过程中数据的完整性。因此,CRC检验PNG图像文件中的计算和应用起到了重要的作用。 ### 回答3: PNG图片CRC(循环冗余校验)检验是一种校验方法,用于验证数据的完整性和准确性。它通过计算数据块中每个字节的值,并执行一系列比特操作来生成一个32位的CRC值。 CRC检验计算过程如下: 1. 初始化CRC寄存器为全1(0xFFFFFFFF); 2. 逐个读取数据块中的每个字节; 3. 将当前字节的值与CRC寄存器的低8位进行异或运算; 4. 重复8次,每次进行一次循环: - 如果CRC寄存器的最低位为1,则右移一位,并将0xEDB88320与CRC寄存器进行异或运算; - 如果CRC寄存器的最低位为0,则只右移一位; 5. 重复步骤2~4,直到处理完所有的字节; 6. 将最终的CRC寄存器的值取反,即得到CRC检验。 这个32位的CRC检验可以用于验证PNG图片中的数据是否被篡改或损坏。当CRC值与存储在PNG文件中的CRC校验相匹配时,说明数据完整和准确;如果不匹配,则说明数据可能已经被修改或损坏。 通过使用CRC检验PNG图片可以确保数据的完整性,提高数据传输的可靠性,并防止数据在传输过程中出现错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值