两个方法,一是用代码数据直接替换原来的图像数据,用PNG等还能压缩和还原,直接打开图像就是杂乱无章的点点。
二是二进制方法,把原图像数据的最后一位按代码的二进制流强置0或1,这个方法原图像看起来没有变化,但要求图像比较大。以下是我实际试验的方法,把js文件写到png图片中,在客户端用canvas解码。
void CintopngDlg::OnBnClickedOk()
{
//把js文件和其它数据文件写入png文件中,在客户端解码
CFile ff;
ff.Open(L"xxxx\\script.min.js", CFile::modeRead);
int jslen = (int)ff.GetLength();
PBYTE ffbuf = new BYTE[jslen];
ff.Read(ffbuf, jslen);
ff.Close(); // 打开原始图像文件,不含js数据,原始图像已经加高,预留js数据区
CImage img;
img.Load(L"xxxx\\images\\png11.png");
int bpp = img.GetBPP(); // 图像颜色位数
ASSERT(bpp == 32); // 一定是32位
bpp /= 8;
int w = img.GetWidth();
int h = img.GetHeight();
ASSERT(jslen < w * h * bpp);
// h > 0 时,图像数据的排列方法是,从左到右,从下到上,
// img.GetBits() 获取图像第一行的地址,在数据区的最后一行。
// 以下方法取图像的最后一行数据,在内存数据区的第一行。
PBYTE buf = PBYTE(img.GetBits()) - w * (h-1) * bpp; // js文件写到图像文件中,即加密,又能压缩传送,所有4的倍数字节是必须ff,为完全不透明,否则js端不能还原正确的值。
// 数据头共12个字节,前三个字节是长度,第四个字节是ff,接着3个字节是长度的hashcode验证长度用,第八个字节是ff,第9-11字节是全部数据(不包括数据头)的hashcode,用于验证数据
PBYTE ps = ffbuf;
PBYTE pd = buf + 12; // 留出数据头
int j = 1;
int hashcode = 0;
for (int i = 0; i < jslen; i++, j++ )
{
if (!(j & 3))
{
*pd++ = 0xff; // 每三个字节后面插补ff,32位颜色的透明度,必须设为完全不透明,否则js端不能还原正确的值。
j++;
}
hashcode = hashcode * 31 + *ps;// hashcode算法http://zhhphappy.iteye.com/blog/1124283
*pd++ = *ps++;
}
for (; j & 3; j++)
*pd++ = 0x20; // 用空格补齐最后一个颜色
*pd = 0xff; // 最后一个透明度 *(PINT)buf = (jslen | 0xff000000); // 长度,不包括插补的ff和补齐的空格
*(PINT)(buf + 4) = ((jslen * 31) | 0xff000000); // 全部数据哈希码,不包括插补的ff和补齐的空格和数据差距
*(PINT)(buf + 8) = (hashcode | 0xff000000); // 全部数据哈希码,不包括插补的ff和补齐的空格和数据差距 delete ffbuf;
ffbuf = 0; img.Save(L"xxxx\\images\\png1.png"); AfxMessageBox(L"编译成功。");
}