您的位置:首页 > 其它

往PNG图像文件写数据

2015-12-26 17:05 274 查看
1. PngStruct.h  // 定义PNG文件的数据结构

#pragma once

// 参考文章:http://blog.chinaunix.net/uid-20622737-id-3130430.html

// 根据PNG文件的定义来说,其文件头位置总是由8位固定的字节来描述的
// 0x89 0x50 0x4E 0x47 0x0D 0x0A 0x1A 0x0A
#define PNG_FILE_HEADER_LEN 8

#define PNG_CHUNK_HEAER_LEN 8

typedef struct tagPngChunkHeader
{
int iLength ; // 4字节 数据块中数据域的长度,其长度不超过(231-1)字节
CHAR szChunkTypeCode[5]; // 4字节,这里写5,是为了给字符串结束符使用的, 数据块类型码由ASCII字母(A-Z和a-z)组成

//BYTE bChunkData[...]; // 数据块数据,可变长度,存储按照Chunk Type Code指定的数据
//int iCRC; // (循环冗余检测)存储用来检测是否有错误的循环冗余码

tagPngChunkHeader()
{
iLength = 0;
ZeroMemory(szChunkTypeCode, sizeof(szChunkTypeCode));
}

}PngChunkHeader;

---------------------------------------------------------------------------------------------------
2.  PngHelper.h   // PNG文件操作接口定义

#pragma once
#include "PngStruct.h"
#include <stdio.h>

#define PNG_CLOSE_FILE(pFile) if(NULL != pFile){fclose(pFile); pFile = NULL;}

BOOL PNG_IsPngFile(LPCTSTR szPngPath); // 判断文件是否是PNG文件

void PNG_PrintfChunkInfo(LPCTSTR szPngPath); // 打印PNG文件数据块信息

BOOL PNG_WriteFile2PNG(LPCTSTR szPngPath, LPCTSTR szFilePath); // 将文件写到PNG文件中去

unsigned long PNG_GetFileSize(LPCTSTR szFilePath); // 获取文件大小

BOOL PNG_RestoreData(LPCTSTR szPngPath, LPCTSTR szDstFilePath); // 还原隐写的数据

BOOL PNG_IsHaveEncData(LPCTSTR szPngPath); // 判断是否已经隐写数据


-----------------------------------------------------------
3. PngHelper.cpp  // PNG操作接口实现文件

#include "StdAfx.h"
#include "PngHelper.h"

#define PNG_INSERT_CHUNK_NAME "XX00"

// PNG文件头标志
BYTE g_pngHeaderFlag[PNG_FILE_HEADER_LEN] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};

// 判断文件是否是PNG文件
BOOL PNG_IsPngFile(LPCTSTR szPngPath)
{
if (szPngPath == NULL)
{
return FALSE;
}

FILE* pFile = NULL;
pFile = fopen(szPngPath, "rb+");
if (pFile == NULL)
{
return FALSE;
}
BYTE bTmp[PNG_FILE_HEADER_LEN] = {0};
fread(bTmp, 1, PNG_FILE_HEADER_LEN, pFile);
fclose(pFile);
pFile = NULL;

return memcmp(bTmp, g_pngHeaderFlag, PNG_FILE_HEADER_LEN) == 0;
}

// 打印PNG文件数据块信息
void PNG_PrintfChunkInfo(LPCTSTR szPngPath)
{
if (!PNG_IsPngFile(szPngPath))
{
return;
}

FILE* pFile = NULL;
pFile = fopen(szPngPath, "rb+");
if (NULL == pFile)
{
return;
}
fseek(pFile, PNG_FILE_HEADER_LEN, SEEK_SET);

PngChunkHeader pngChunkHdr;
CHAR szLog[1024] = {0};
BYTE bTmpLen[4] = {0};
while(TRUE)
{
fread(&pngChunkHdr, 1, PNG_CHUNK_HEAER_LEN, pFile);

// 00 00 00 0D
bTmpLen[0] = (pngChunkHdr.iLength & 0xFF000000) / 0x1000000;
bTmpLen[1] = (pngChunkHdr.iLength & 0xFF0000) / 0x10000;
bTmpLen[2] = (pngChunkHdr.iLength & 0xFF00) / 0x100;
bTmpLen[3] = (pngChunkHdr.iLength & 0xFF);

pngChunkHdr.iLength = *(int*)bTmpLen;
_stprintf(szLog, "pngChunkHdr.iLength=%d, pngChunkHdr.szChunkTypeCode=%s \r\n", pngChunkHdr.iLength, pngChunkHdr.szChunkTypeCode);
OutputDebugString(szLog);

// 判断是否是最后一个数据块
if (0 == pngChunkHdr.iLength && _tcscmp(pngChunkHdr.szChunkTypeCode, "IEND") == 0)
{
break;
}

// 移动到下一个数据块
fseek(pFile, pngChunkHdr.iLength + 4, SEEK_CUR);
}

fclose(pFile);
pFile = NULL;
}

// 将文件写到PNG文件中去
BOOL PNG_WriteFile2PNG(LPCTSTR szPngPath, LPCTSTR szFilePath)
{
if (szPngPath == NULL || szFilePath == NULL)
{
return FALSE;
}
if (!PNG_IsPngFile(szPngPath))
{
return FALSE;
}

CHAR szDstFile[MAX_PATH] = {0};
_tcsncpy(szDstFile, szPngPath, _tcslen(szPngPath) - _tcslen(".png"));
_tcscat(szDstFile, "隐写.png");

FILE* pfilePng = NULL;
FILE* pfileEnc = NULL;
FILE* pfileDst = NULL;
pfilePng = fopen(szPngPath, "rb+");
pfileEnc = fopen(szFilePath, "rb+");
pfileDst = fopen(szDstFile, "wb+");
if (NULL == pfilePng || NULL == pfileEnc || NULL == pfileDst)
{
PNG_CLOSE_FILE(pfilePng);
PNG_CLOSE_FILE(pfileEnc);
PNG_CLOSE_FILE(pfileDst);
return FALSE;
}

// PNG头
BYTE bPngHdr[PNG_FILE_HEADER_LEN] = {0};
fread(bPngHdr, 1, PNG_FILE_HEADER_LEN, pfilePng);
fwrite(bPngHdr, 1, PNG_FILE_HEADER_LEN, pfileDst);

// PNG数据块
PngChunkHeader pngChunkHdr;
BYTE bTmpLen[4] = {0};
fseek(pfilePng, PNG_FILE_HEADER_LEN, SEEK_SET);
LONG liPos = 0;
while(TRUE)
{
fread(&pngChunkHdr, 1, PNG_CHUNK_HEAER_LEN, pfilePng);
fwrite(&pngChunkHdr, 1, PNG_CHUNK_HEAER_LEN, pfileDst);

// 00 00 00 0D
bTmpLen[0] = (pngChunkHdr.iLength & 0xFF000000) / 0x1000000;
bTmpLen[1] = (pngChunkHdr.iLength & 0xFF0000) / 0x10000;
bTmpLen[2] = (pngChunkHdr.iLength & 0xFF00) / 0x100;
bTmpLen[3] = (pngChunkHdr.iLength & 0xFF);

pngChunkHdr.iLength = *(int*)bTmpLen;

PBYTE bTmpData = new BYTE[pngChunkHdr.iLength + 4];
if (bTmpData != NULL)
{
fread(bTmpData, 1, pngChunkHdr.iLength + 4, pfilePng);
fwrite(bTmpData, 1, pngChunkHdr.iLength + 4, pfileDst);
}
if (bTmpData != NULL)
{
delete[] bTmpData;
bTmpData = NULL;
}

static BOOL isWrite = FALSE;
if (_tcscmp(pngChunkHdr.szChunkTypeCode, "IDAT") == 0 && FALSE == isWrite)
{
isWrite = TRUE;

unsigned long ulFileLen = PNG_GetFileSize(szFilePath);
pngChunkHdr.iLength = ulFileLen;
_tcscpy(pngChunkHdr.szChunkTypeCode, PNG_INSERT_CHUNK_NAME);
fwrite(&pngChunkHdr, 1, PNG_CHUNK_HEAER_LEN, pfileDst);

PBYTE bTmpData = new BYTE[ulFileLen];
fread(bTmpData, 1, ulFileLen, pfileEnc);
fwrite(bTmpData, 1, ulFileLen, pfileDst);
}

// 判断是否是最后一个数据块
if (0 == pngChunkHdr.iLength && _tcscmp(pngChunkHdr.szChunkTypeCode, "IEND") == 0)
{
break;
}

// 移动到下一个数据块
/*liPos = ftell(pfilePng);
fseek(pfilePng, pngChunkHdr.iLength + 0, SEEK_CUR);
liPos = ftell(pfilePng);*/

}

PNG_CLOSE_FILE(pfilePng);
PNG_CLOSE_FILE(pfileEnc);
PNG_CLOSE_FILE(pfileDst);

return TRUE;
}

// 取文件大小,单位字节
unsigned long PNG_GetFileSize(LPCTSTR szFilePath)
{
unsigned long size;
FILE *fp = fopen(szFilePath,"rb+");
if(fp == NULL)
{
return 0;
}
fseek(fp, 0L, SEEK_END);
size = ftell(fp);
fclose(fp);
return size;
}

// 还原隐写的数据
BOOL PNG_RestoreData(LPCTSTR szPngPath, LPCTSTR szDstFilePath)
{
if(NULL == szPngPath || NULL == szDstFilePath)
{
return FALSE;
}
if (!PNG_IsHaveEncData(szPngPath))
{
return FALSE;
}

FILE* pfilePng = NULL;
FILE* pfileDst = NULL;
pfilePng = fopen(szPngPath, "rb+");
pfileDst = fopen(szDstFilePath, "wb+");
if (NULL == pfilePng || NULL == pfileDst)
{
PNG_CLOSE_FILE(pfilePng);
PNG_CLOSE_FILE(pfileDst);
return FALSE;
}

// PNG头
BYTE bPngHdr[PNG_FILE_HEADER_LEN] = {0};
fread(bPngHdr, 1, PNG_FILE_HEADER_LEN, pfilePng);
fwrite(bPngHdr, 1, PNG_FILE_HEADER_LEN, pfileDst);

// PNG数据块
PngChunkHeader pngChunkHdr;
BYTE bTmpLen[4] = {0};
fseek(pfilePng, PNG_FILE_HEADER_LEN, SEEK_SET);
LONG liPos = 0;
while(TRUE)
{
fread(&pngChunkHdr, 1, PNG_CHUNK_HEAER_LEN, pfilePng);

// 00 00 00 0D
bTmpLen[0] = (pngChunkHdr.iLength & 0xFF000000) / 0x1000000;
bTmpLen[1] = (pngChunkHdr.iLength & 0xFF0000) / 0x10000;
bTmpLen[2] = (pngChunkHdr.iLength & 0xFF00) / 0x100;
bTmpLen[3] = (pngChunkHdr.iLength & 0xFF);

pngChunkHdr.iLength = *(int*)bTmpLen;

// 找到隐写的数据块
if (_tcscmp(pngChunkHdr.szChunkTypeCode, PNG_INSERT_CHUNK_NAME) == 0)
{
unsigned long ulFileLen = pngChunkHdr.iLength;
PBYTE bTmpData = new BYTE[ulFileLen];
fread(bTmpData, 1, ulFileLen, pfilePng);
fwrite(bTmpData, 1, ulFileLen, pfileDst);
break;
}

// 判断是否是最后一个数据块
if (0 == pngChunkHdr.iLength && _tcscmp(pngChunkHdr.szChunkTypeCode, "IEND") == 0)
{
break;
}

// 移动到下一个数据块
fseek(pfilePng, pngChunkHdr.iLength + 4, SEEK_CUR);

}

PNG_CLOSE_FILE(pfilePng);
PNG_CLOSE_FILE(pfileDst);

return TRUE;
}

// 判断是否已经隐写数据
BOOL PNG_IsHaveEncData(LPCTSTR szPngPath)
{
if(szPngPath == NULL)
{
return FALSE;
}

if (!PNG_IsPngFile(szPngPath))
{
return FALSE;
}

FILE* pFile = NULL;
pFile = fopen(szPngPath, "rb+");
if (NULL == pFile)
{
return FALSE;
}
fseek(pFile, PNG_FILE_HEADER_LEN, SEEK_SET);

BOOL bRet = FALSE;
PngChunkHeader pngChunkHdr;
CHAR szLog[1024] = {0};
BYTE bTmpLen[4] = {0};
while(TRUE)
{
fread(&pngChunkHdr, 1, PNG_CHUNK_HEAER_LEN, pFile);

// 00 00 00 0D
bTmpLen[0] = (pngChunkHdr.iLength & 0xFF000000) / 0x1000000;
bTmpLen[1] = (pngChunkHdr.iLength & 0xFF0000) / 0x10000;
bTmpLen[2] = (pngChunkHdr.iLength & 0xFF00) / 0x100;
bTmpLen[3] = (pngChunkHdr.iLength & 0xFF);

pngChunkHdr.iLength = *(int*)bTmpLen;

// 标志数据块
if (_tcscmp(pngChunkHdr.szChunkTypeCode, PNG_INSERT_CHUNK_NAME) == 0)
{
bRet = TRUE;
break;
}

// 判断是否是最后一个数据块
if (0 == pngChunkHdr.iLength && _tcscmp(pngChunkHdr.szChunkTypeCode, "IEND") == 0)
{
break;
}

// 移动到下一个数据块
fseek(pFile, pngChunkHdr.iLength + 4, SEEK_CUR);
}

fclose(pFile);
pFile = NULL;

return bRet;
}


-------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息