PNG是一种非常流行的图片格式,它不仅支持透明效果,而且图片数据经过了压缩处理,所以广泛用于web等应用。

PNG的文件格式:

  PNG文件中的数据,总是以一个固定的8个字节开头:

    (图片来自http://blog.csdn.net/bisword/article/details/2777121

  除此之外,PNG的其他数据都是以数据块的方式组织,它们被分为标准数据块和辅助数据块,其中的辅助数据块是可选的。关键数据块包含我们必须的图片信息,我们之后要重点解析的也是关键数据块。

  

  (图片来自http://blog.csdn.net/bisword/article/details/2777121

每种数据块的结构:

  

    (图片来自http://blog.csdn.net/bisword/article/details/2777121

  Length:该数据块的中Chunk Data的长度;

  Chunk Type Code:数据类型,就是指上面提到的IHDR,IEND等;

  Chunk Data:数据区域,如果是IDAT,就表示存储的还未解压的图片数据;

  CRC:循环冗余效验码;

具体实现:(实现中没有处理png数据中变形的情况,部分头中的宏定义来自libpng,实例不具备实用性,仅作参考)

头文件:

 #ifndef __PNG__
#define __PNG__ #include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "zlib/zlib.h" /**
* 类型标志
*/
#define PNG_FLAG_HEX "89504E470D0A1A0A" /**
* 数据块类型
*/
#define DATA_CHUNK_TYPE_IHDR "IHDR"
#define DATA_CHUNK_TYPE_IDAT "IDAT"
#define DATA_CHUNK_TYPE_IEND "IEND"
#define DATA_CHUNK_TYPE_tEXt "tEXt"
#define DATA_CHUNK_TYPE_iTXt "iTXt" /**
* 过滤方式
*/
#define DATA_FILTER_TYPE_DEFAULT 0
#define DATA_FILTER_TYPE_ADD_ROW 1
#define DATA_FILTER_TYPE_ADD_UP 2
#define DATA_FILTER_TYPE_AVERGE 3
#define DATA_FILTER_TYPE_PAETH 4 /* color type masks */
#define PNG_COLOR_MASK_PALETTE 1
#define PNG_COLOR_MASK_COLOR 2
#define PNG_COLOR_MASK_ALPHA 4 /* color types. Note that not all combinations are legal */
#define PNG_COLOR_TYPE_GRAY 0
#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) #define RGB_USE_ALPHA(vr, vg, vb, va) \
(unsigned)(((unsigned)((unsigned char)(vr) * ((unsigned char)(va) + )) >> ) | \
((unsigned)((unsigned char)(vg) * ((unsigned char)(va) + ) >> ) << ) | \
((unsigned)((unsigned char)(vb) * ((unsigned char)(va) + ) >> ) << ) | \
((unsigned)(unsigned char)(va) << )) /**
* 一次解压图片数据的限制
*/
#define DECOMPRESSION_MAX_BYTES 8192 /**
* 数据块信息
*/
typedef struct _DataChunkHeader
{
// 数据长度
unsigned char length[];
// 数据类型
unsigned char type[];
} DataChunkHeader; /**
* IHDR数据
*/
typedef struct _IDHRData
{
unsigned char width[];
unsigned char height[];
unsigned char bitDepth[];
unsigned char colorType[];
unsigned char compressionMethod[];
unsigned char filterMethod[];
unsigned char interlaceMethod[];
} IDHRData; /**
* PNG图片类
*/
class PNG
{
public:
PNG();
PNG(const char* filePath); ~PNG(); int getWindth();
int getHeight(); /**
* 获取图片宽度
*/
unsigned char* getImageData(); private:
int m_width;
int m_height; unsigned char m_bitDepth;
unsigned char m_colorType;
unsigned char m_compressionMethod;
unsigned char m_filterMethod;
unsigned char m_interlaceMethod;
unsigned char m_chanels; unsigned char* m_imageData; /**
* 从文件加载图片数据
*/
bool loadImageDataFromFile(const char* filePath); /**
* 解析数值
*/
int parseNumber(const unsigned char* data, int len); /**
* 解压数据
*/
int decompressData(z_stream* zStream, unsigned char* data, int dataLen, int leftLen, FILE *pFile); /**
* 生成图片数据
*/
void generateImageData(unsigned char* data, unsigned long dataLen); /**
* 默认的过滤方式
*/
void defaultFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); /**
* 当前行相加的过滤方式
*/
void addCurrentRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); /**
* 前一行相加的过滤方式
*/
void addUpRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); /**
* 平均的过滤方式
*/
void avergeFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); /**
* paeth的过滤方式
*/
void paethFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); /**
* 解析IHDR数据
*/
void parseIHDRData(DataChunkHeader& dataChunkHeader, FILE* pFile); /**
* 解析IDAT数据
*/
void parseIDATData(DataChunkHeader& dataChunkHeader, FILE* pFile); /**
* 解析IEND数据
*/
void parseIENDData(DataChunkHeader& dataChunkHeader, FILE *pFile); /**
* 解析其他数据
*/
void parseCommonData(DataChunkHeader& dataChunkHeader, FILE *pFile);
}; #endif

cpp文件:

 #include "png.h"
#include "utils/cUtil.h"
#include <stdlib.h> #include <windows.h> /**
* 默认构造函数
*/
PNG::PNG()
{
this->m_width = ;
this->m_height = ; this->m_imageData = ;
} /**
* 构造函数
* @param filePath 图片路径
*/
PNG::PNG(const char *filePath)
{
this->m_width = ;
this->m_height = ; this->loadImageDataFromFile(filePath);
} /**
* 析构函数
*/
PNG::~PNG()
{ } /**
* 从文件加载图片数据
*/
bool PNG::loadImageDataFromFile(const char* filePath)
{
FILE* pFile = fopen(filePath, "rb");
if (!pFile)
return false; // 解析PNG标志
char flag[];
char hexFlag[];
fread(flag, , , pFile);
toHexStr(flag, , hexFlag);
if (strcmp(hexFlag, PNG_FLAG_HEX) != )
return false; // 解析图片数据
DataChunkHeader dataChunkHeader;
char dataChunkHeaderType[];
do {
fread(&dataChunkHeader, , sizeof(DataChunkHeader), pFile); memcpy(dataChunkHeaderType, dataChunkHeader.type, );
dataChunkHeaderType[] = '\0'; // IHDR
if ( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IHDR) == ) {
this->parseIHDRData(dataChunkHeader, pFile);
}
// IDAT
else if ( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IDAT) == ) {
this->parseIDATData(dataChunkHeader, pFile);
}
// IEND
else if ( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IEND) == ) {
this->parseIENDData(dataChunkHeader, pFile);
}
// 其他数据
else {
this->parseCommonData(dataChunkHeader, pFile);
}
} while( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IEND) != ); int i = ; return true;
} /**
* 解析数值
*/
int PNG::parseNumber(const unsigned char* data, int len)
{
char localNum[]; bool isLittleEndian = checkEndian();
for (int i = ; i<; i++) {
char ch; if (isLittleEndian) {
if (i <= len-)
ch = data[len - - i];
else
ch = '\0';
}
else {
if (i <= len-)
ch = data[i];
else
ch = '\0';
}
localNum[i] = ch;
} int num;
memcpy(&num, localNum, );
return num;
} /**
* 解析IHDR数据
*/
void PNG::parseIHDRData(DataChunkHeader& dataChunkHeader, FILE* pFile)
{
int dataLen = this->parseNumber(dataChunkHeader.length, ); IDHRData idhrData;
char crc[]; fread(&idhrData, , sizeof(IDHRData), pFile);
fread(crc, , , pFile); this->m_width = this->parseNumber(idhrData.width, );
this->m_height = this->parseNumber(idhrData.height, );
this->m_bitDepth = this->parseNumber(idhrData.bitDepth, );
this->m_colorType = this->parseNumber(idhrData.colorType, );
this->m_compressionMethod = this->parseNumber(idhrData.compressionMethod, );
this->m_filterMethod = this->parseNumber(idhrData.filterMethod, );
this->m_interlaceMethod = this->parseNumber(idhrData.interlaceMethod, );
this->m_chanels = ; switch (this->m_colorType) {
case PNG_COLOR_TYPE_GRAY:
case PNG_COLOR_TYPE_PALETTE:
this->m_chanels = ;
break;
case PNG_COLOR_TYPE_RGB:
this->m_chanels = ;
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
this->m_chanels = ;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
this->m_chanels = ;
break;
default:
this->m_chanels = ;
break;
}
} /**
* 解压数据
*/
int PNG::decompressData(z_stream* zStream, unsigned char* data, int dataLen, int leftLen, FILE *pFile)
{
int result = ; int leftBytesCount = leftLen;
int avail_out = -;
do {
if (zStream->avail_in == ) {
if (avail_out == )
break;
else {
if (leftBytesCount == ) {
DataChunkHeader dataChunkHeader;
fread(&dataChunkHeader, , sizeof(DataChunkHeader), pFile); int newDataLen = this->parseNumber(dataChunkHeader.length, );
unsigned char* newData = new unsigned char[dataLen + newDataLen];
char crc[]; fread(newData + dataLen, , newDataLen, pFile);
fread(crc, , , pFile);
memcpy(newData, data, dataLen); delete data;
data = newData; zStream->next_in = newData + dataLen;
zStream->avail_in = newDataLen; dataLen = dataLen + newDataLen; return this->decompressData(zStream, data, dataLen, , pFile);
}
} // 导出数据是否超过限制
if (leftBytesCount > DECOMPRESSION_MAX_BYTES)
zStream->avail_in = DECOMPRESSION_MAX_BYTES;
else
zStream->avail_in = leftBytesCount; leftBytesCount -= zStream->avail_in;
} if (avail_out > )
zStream->avail_out = avail_out;
else
zStream->avail_out = m_width * + ; result = inflate(zStream, Z_NO_FLUSH);
if (result != Z_OK)
break; avail_out = zStream->avail_out;
} while (zStream->avail_in >= ); return result;
} /**
* 生成图片数据
*/
void PNG::generateImageData(unsigned char* data, unsigned long dataLen)
{
// 行字节数
int rowBytes = this->m_chanels * this->m_width; // 初始化图片数据
this->m_imageData = new unsigned char[rowBytes * this->m_height]; unsigned char* pImageData = this->m_imageData;
unsigned char* pRowData = data; for (int rowIndex = ; rowIndex < this->m_height; rowIndex++) {
// 过滤类型
unsigned char filterType = pRowData[]; pRowData += ; switch (filterType) {
// 不需要过滤处理
case DATA_FILTER_TYPE_DEFAULT:
this->defaultFilterType(pImageData, pRowData, rowBytes);
break;
// 当前行相加
case DATA_FILTER_TYPE_ADD_ROW:
this->addCurrentRowFilterType(pImageData, pRowData, rowBytes);
break;
// 和前一行相加
case DATA_FILTER_TYPE_ADD_UP:
this->addUpRowFilterType(pImageData, pRowData, rowBytes);
break;
// 求平均
case DATA_FILTER_TYPE_AVERGE:
this->avergeFilterType(pImageData, pRowData, rowBytes);
break;
// Paeth
case DATA_FILTER_TYPE_PAETH:
this->paethFilterType(pImageData, pRowData, rowBytes);
break;
// 类型错误
default:
break;
} pImageData += rowBytes;
pRowData += rowBytes; char text[];
sprintf(text, "filter type:%d, rowIndex:%d \n", filterType, rowIndex);
OutputDebugString(text);
} int channel = rowBytes / this->m_width;
if (channel == ) {
unsigned int *tmp = (unsigned int *)this->m_imageData; for (unsigned short i = ; i < this->m_height; i++) {
for (unsigned int j = ; j < rowBytes; j+=) {
unsigned int offset = i * rowBytes + j; *tmp++ = RGB_USE_ALPHA(
this->m_imageData[offset],
this->m_imageData[offset+],
this->m_imageData[offset+],
this->m_imageData[offset+]
);
}
}
}
} /**
* 默认的过滤方式
*/
void PNG::defaultFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes)
{
for (int i = ; i < rowBytes; i++) {
*pImageData++ = *pRowData++;
}
} /**
* 当前行相加的过滤方式
*/
void PNG::addCurrentRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes)
{
for (int i = ; i < rowBytes; i++) {
if (i == ) {
memcpy(pImageData, pRowData, );
i += ;
pImageData += ;
pRowData += ;
}
else {
*pImageData++ = (unsigned char)(((int)*(pRowData++) + (int)*(pImageData-)) & 0xFF);
}
}
} /**
* 前一行相加的过滤方式
*/
void PNG::addUpRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes)
{
for (int i = ; i < rowBytes; i++) {
*pImageData++ = (unsigned char)(((int)*(pRowData++) + (int)*(pImageData-rowBytes)) & 0xFF);
}
} /**
* 平均的过滤方式
*/
void PNG::avergeFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes)
{
for (int i = ; i < rowBytes; i++) {
int averge = ; if (i <= ) {
averge = ((int)*(pImageData-rowBytes)) / ; *pImageData++ = (unsigned char)((averge + (int)*(pRowData++)) & 0xFF);
}
else {
averge = (((int)*(pImageData-)) + ((int)*(pImageData-rowBytes))) / ; *pImageData++ = (unsigned char)((averge + (int)*(pRowData++)) & 0xFF);
}
}
} /**
* paeth的过滤方式
*/
int Paeth(int a, int b, int c)
{
int p = a + b - c;
int pa = abs(p - a);
int pb = abs(p - b);
int pc = abs(p - c); int Paeth;
if(pa <= pb && pa <= pc)
Paeth = a;
else if (pb <= pc)
Paeth = b;
else
Paeth = c;
return Paeth ;
}
void PNG::paethFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes)
{
for (int i = ; i < rowBytes; i++) {
if (i <= ) {
*pImageData++ = (unsigned char)(((int)*(pRowData++) + (int)*(pImageData-rowBytes)) & 0xFF);
}
else {
unsigned char left = *(pImageData - );
unsigned char up = *(pImageData - rowBytes);
unsigned char leftUp = *(pImageData - rowBytes - ); int value = Paeth((int)left, (int)up, (int)leftUp); *pImageData++ = (unsigned char)(((int)*(pRowData++) + value) & 0xFF);
}
}
} /**
* 解析IDAT数据
*/
void PNG::parseIDATData(DataChunkHeader& dataChunkHeader, FILE* pFile)
{
// 解压后的图片数据
unsigned char* imageData = new unsigned char[m_width * m_height * ]; int dataLen = this->parseNumber(dataChunkHeader.length, );
// 解压前的图片数据
unsigned char* data = new unsigned char[dataLen];
char crc[];
// 提取数据
fread(data, , dataLen, pFile);
fread(crc, , , pFile); // 存放临时的解压数据
unsigned long decompressDataLen = m_width * m_height * + m_height;
unsigned char* decompressData = new unsigned char[decompressDataLen]; z_stream* zStream = new z_stream();
zStream->next_in = data;
zStream->next_out = decompressData; inflateInit(zStream); // 解压数据
this->decompressData(zStream, data, dataLen, dataLen, pFile);
// 生成图片数据
this->generateImageData(decompressData, decompressDataLen); /*
int result = 0;
// 开始解压数据
int leftBytesCount = dataLen;
int avail_out = -1;
do {
if (zStream->avail_in == 0) {
if (avail_out == 0)
break;
else {
if (leftBytesCount == 0) { }
} // 导出数据是否超过限制
if (leftBytesCount > DECOMPRESSION_MAX_BYTES)
zStream->avail_in = DECOMPRESSION_MAX_BYTES;
else
zStream->avail_in = leftBytesCount; leftBytesCount = dataLen - zStream->avail_in;
} if (avail_out > 0)
zStream->avail_out = avail_out;
else
zStream->avail_out = m_width * 4 + 1; result = inflate(zStream, Z_NO_FLUSH);
if (result != Z_OK)
break; avail_out = zStream->avail_out;
} while (zStream->avail_in >= 0);
// 数据解压是否成功
if (result == Z_STREAM_END) {
int i = 1;
}
*/
} /**
* 解析IEND数据
*/
void PNG::parseIENDData(DataChunkHeader& dataChunkHeader, FILE *pFile)
{
char crc[];
fread(crc, , , pFile);
} /**
* 解析其他数据
*/
void PNG::parseCommonData(DataChunkHeader& dataChunkHeader, FILE *pFile)
{
int dataLen = this->parseNumber(dataChunkHeader.length, );
fseek(pFile, dataLen + , SEEK_CUR);
} /**
* 获取图片宽度
*/
unsigned char* PNG::getImageData()
{
return this->m_imageData;
} /**
* 获取图片宽度
*/
int PNG::getWindth()
{
return this->m_width;
} /**
* 获取图片高度
*/
int PNG::getHeight()
{
return this->m_height;
}

如果需要绘制图片,可以使用opengl库

参考代码:

 glViewport(, , winWidth, winHeight);    

 glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0f, winWidth - 1.0, 0.0, winHeight - 1.0, -10.0, 10.0); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glEnable(GL_TEXTURE_2D); int width = png->getWindth();
int height = png->getHeight();
unsigned char* data = png->getImageData(); GLuint name1;
glGenTextures(, &name1);
glBindTexture(GL_TEXTURE_2D, name1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, , GL_RGBA, width, height, ,GL_RGBA, GL_UNSIGNED_BYTE, data);
glBegin(GL_POLYGON);
glTexCoord2f(, );
glVertex3d(, , );
glTexCoord2f(, );
glVertex3d(, , );
glTexCoord2f(, );
glVertex3d(, , );
glTexCoord2f(, );
glVertex3d(, , );
glEnd();

PNG图片数据解析的更多相关文章

  1. Silverlight项目笔记7:xml/json数据解析、TreeView、引用类型与数据绑定错误、图片加载、虚拟目录设置、silverlight安全机制引发的问题、WebClient缓存问题

    1.xml/json数据解析 (1)xml数据解析 使用WebClient获取数据,获取到的数据实例化为一个XDocument,使用XDocument的Descendants(XName)方法获得对应 ...

  2. Gprinter热敏打印机光栅位图点阵数据解析工具

    最近参与的项目有一个需求,解析佳博热敏打印机的光栅位图点阵数据并保存为图片文件.数据是通过Bus Hound抓取的,如下图所示. 其中1b 40为初始化打印机的指令,对应的ASCII码为ESC @,1 ...

  3. iOS开发——实战篇Swift篇&UItableView结合网络请求,多线程,数据解析,MVC实战

    UItableView结合网络请求,多线程,数据解析,MVC实战 学了这么久的swift都没有做过什么东西,今天就以自己的一个小小的联系,讲一下,怎么使用swift在实战中应用MVC,并且结合后面的高 ...

  4. BLE 广播数据解析

    从上一篇GATT Profile 简介中提到过,BLE 设备工作的第一步就是向外广播数据.广播数据中带有设备相关的信息.本文主要说一下 BLE 的广播中的数据的规范以及广播包的解析. 广播模式 BLE ...

  5. iOS学习——JSON数据解析(十一)

    在之前的<iOS学习——xml数据解析(九)>介绍了xml数据解析,这一篇简单介绍一下Json数据解析.JSON 即 JavaScript Object Natation,它是一种轻量级的 ...

  6. Python网络爬虫之三种数据解析方式

    1. 正则解析 正则例题 import re # string1 = """<div>静夜思 # 窗前明月光 # 疑是地上霜 # 举头望明月 # 低头思故乡 ...

  7. 05.Python网络爬虫之三种数据解析方式

    引入 回顾requests实现数据爬取的流程 指定url 基于requests模块发起请求 获取响应对象中的数据 进行持久化存储 其实,在上述流程中还需要较为重要的一步,就是在持久化存储之前需要进行指 ...

  8. 初探iOS网络开发,数据解析。

    通过大众点评平台开发来简单了解一下,oc的网络编程和数据解析(json) 首先我们需要到大大众点评开发者平台申请一个key.http://developer.dianping.com/app/tech ...

  9. 数据解析之xpath

    一.环境安装 下载lxml pip install lxml 二.使用 XPath 使用路径表达式来选取 XML 文档中的节点或节点集.节点是通过沿着路径 (path) 或者步 (steps) 来选取 ...

随机推荐

  1. sql server 2012 如何收缩事务日志

    sql2008不再支持 BACKUP LOG 数据库名 WITH NO_LOG   语句 BACKUP Log zxta with no_log 截断事务日志 sql2008 提示错误如下 BACKU ...

  2. 返回顶部(解决IE6固定定位)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. PLSQL_基础系列05_视图控制WITH CHECK OPTION(案例)

    2014-12-09 Created By BaoXinjian

  4. js实现的新闻列表垂直滚动实现详解

    js实现的新闻列表垂直滚动实现详解:新闻列表垂直滚动效果在大量的网站都有应用,有点自然是不言而喻的,首先由于网页的空间有限,使用滚动代码可以使用最小的空间提供更多的信息量,还有让网页有了动态的效果,更 ...

  5. sphinx 全文搜索引擎安装与配置

    sphinx 全文搜索引擎 sphinx的安装与配置 ------------------------------------------------------------------------- ...

  6. jdk与jre的区别

    很多程序员已经干了一段时间java了依然不明白jdk与jre的区别.JDK就是Java Development Kit.简单的说JDK是面向开发人员使用的SDK,它提供了Java的开发环境和运行环境. ...

  7. Python深入05 装饰器

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 装饰器(decorator)是一种高级Python语法.装饰器可以对一个函数.方法 ...

  8. Android Studio 系列教程(转载)

    史上最详细的Android Studio系列教程一--下载和安装:http://segmentfault.com/a/1190000002401964史上最详细的Android Studio系列教程二 ...

  9. Oracle中增加,修改,删除表中的列

    有些时候,当一个表已经建好,并且已经使用后,发现需要对表结构进行修改,这个时候就要对表中的列进行增删查改操作. 为表增加新列: ALTER TABLE table_name ADD ( column_ ...

  10. Javascript同源策略对context.getImageData的影响

    在本机测试HTML5 Canvas程序的时候,如果用context.drawImage()后再用context.getImageData()获取图片像素数据的时候会抛出错:SECURITY_ERR: ...