PNG-的IDAT解析

     前段时间在做PNG的解析问题,对于PNG格式基于字节的读写有了一定了解,此文记录如何解析PNG图片的数据字段,也就是真实像素的二进制解析,PNG的data数据基本数据块的组成为 :Filter +  压缩数据。(这里除去校验头尾),二进制读取自己写即可,文件头的解析等,本文也不记录,可以参看https://www.w3.org/TR/PNG/#9Filter或者参考这里中文解释https://www.jianshu.com/p/ecacf2f60cb2,这里面有一些介绍,原理已经讲的很明白了,但是没有代码的干货,这里补充一下,下面的算法是假设我们已经将压缩的数据使用zlip压缩解压了,但是需要根据Filter 类型去还原像素值,算法如下:
		/*
		   -------------
		   |C | B | D |
		   ------------
		   |A | x |   |
		   ------------

		Type	Name	Filter Function	Reconstruction Function
		0	None	Filt(x) = Orig(x)	Recon(x) = Filt(x)
		1	Sub	Filt(x) = Orig(x) - Orig(a)	Recon(x) = Filt(x) + Recon(a)
		2	Up	Filt(x) = Orig(x) - Orig(b)	Recon(x) = Filt(x) + Recon(b)
		3	Average	Filt(x) = Orig(x) - floor((Orig(a) + Orig(b)) / 2)	Recon(x) = Filt(x) + floor((Recon(a) + Recon(b)) / 2)
		4	Paeth	Filt(x) = Orig(x) - PaethPredictor(Orig(a), Orig(b), Orig(c))	Recon(x) = Filt(x) + PaethPredictor(Recon(a), Recon(b), Recon(c))

		x	the byte being filtered;
		a	the byte corresponding to x in the pixel immediately before the pixel containing x(or the byte immediately before x, when the bit depth is less than 8);
		b	the byte corresponding to x in the previous scanline;
		c	the byte corresponding to b in the pixel immediately before the pixel containing b(or the byte immediately before b, when the bit depth is less than 8).
		*/
		enum FilterTypes
		{
				eFTNone =0,
				eFTSub = 1,
				eFTUp =2,
				eFTAverage = 3,
				eFTPaeth =4,
		};

void GsPNG::Filtering(FilterTypes etype, unsigned char * row, int length)
{
	if (m_Type != eRGBA8)//先实现常用的rgba,这里其实不用管通道顺序,只需要知道几个通道即可,
		return; 
	m_CacheBefore.Allocate(length);
	
	switch (etype)
	{
		//原值不处理
		case eFTNone:break;
		case eFTSub:
		{
			for (int i = 4; i < length; i += 4)
			{
				row[i]	+= row[i - 4];
				row[i + 1] += row[i - 3];
				row[i + 2] += row[i - 2];
				row[i + 3] += row[i - 1];
			}
		}
		break;
		case eFTUp: 
		{
			for (int i = 0; i < length; i += 4)
			{
				row[i] += m_CacheBefore.BufferHead()[i];
				row[i + 1] += m_CacheBefore.BufferHead()[i + 1];
				row[i + 2] += m_CacheBefore.BufferHead()[i + 2];
				row[i + 3] += m_CacheBefore.BufferHead()[i + 3];
			}
		}
		break;
		case eFTAverage:
		{
			row[0] += floor((m_CacheBefore.BufferHead()[0] + 0) / 2);
			row[1] += floor((m_CacheBefore.BufferHead()[1] + 0) / 2);
			row[2] += floor((m_CacheBefore.BufferHead()[2] + 0) / 2);
			row[3] += floor((m_CacheBefore.BufferHead()[3] + 0) / 2);
			for (int i = 4; i < length; i += 4)
			{
				row[i] += floor((m_CacheBefore.BufferHead()[i]+ row[i - 4])/2);
				row[i + 1] += floor((m_CacheBefore.BufferHead()[i+1] + row[i - 3]) / 2);
				row[i + 2] += floor((m_CacheBefore.BufferHead()[i+2] + row[i - 2]) / 2);
				row[i + 3] += floor((m_CacheBefore.BufferHead()[i+3] + row[i - 1]) / 2);
			}
		}
		break;
		case eFTPaeth:
		{
			//从0开始,超出的像素用0 代替,这里在官网有一排小字介绍
			row[0] += PaethPredictor(0, m_CacheBefore.BufferHead()[0], 0);
			row[1] += PaethPredictor(0, m_CacheBefore.BufferHead()[1], 0);
			row[2] += PaethPredictor(0, m_CacheBefore.BufferHead()[2], 0);
			row[3] += PaethPredictor(0, m_CacheBefore.BufferHead()[3], 0);

			for (int i = 4; i < length; i += 4)
			{
				row[i] += PaethPredictor(row[i - 4], m_CacheBefore.BufferHead()[i], m_CacheBefore.BufferHead()[i - 4]);
				row[i + 1] += PaethPredictor(row[i - 3], m_CacheBefore.BufferHead()[i+1], m_CacheBefore.BufferHead()[i - 3]);
				row[i + 2] += PaethPredictor(row[i - 2], m_CacheBefore.BufferHead()[i+2], m_CacheBefore.BufferHead()[i - 2]);
				row[i + 3] += PaethPredictor(row[i - 1], m_CacheBefore.BufferHead()[i+3], m_CacheBefore.BufferHead()[i - 1]);
			}
		}
		break;
	default:
		break;
	}
	//记录一下,下次取BC的值
	m_CacheBefore.Copy(row, length);
}
再次重申上面的代码是将已经zlip解压后的数据使用Filtering 过滤反算后得到真实的像素值的过程,正算一样的,这里就不做过多描述


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值