PNG图片数据解析
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图片数据解析的更多相关文章
- Silverlight项目笔记7:xml/json数据解析、TreeView、引用类型与数据绑定错误、图片加载、虚拟目录设置、silverlight安全机制引发的问题、WebClient缓存问题
1.xml/json数据解析 (1)xml数据解析 使用WebClient获取数据,获取到的数据实例化为一个XDocument,使用XDocument的Descendants(XName)方法获得对应 ...
- Gprinter热敏打印机光栅位图点阵数据解析工具
最近参与的项目有一个需求,解析佳博热敏打印机的光栅位图点阵数据并保存为图片文件.数据是通过Bus Hound抓取的,如下图所示. 其中1b 40为初始化打印机的指令,对应的ASCII码为ESC @,1 ...
- iOS开发——实战篇Swift篇&UItableView结合网络请求,多线程,数据解析,MVC实战
UItableView结合网络请求,多线程,数据解析,MVC实战 学了这么久的swift都没有做过什么东西,今天就以自己的一个小小的联系,讲一下,怎么使用swift在实战中应用MVC,并且结合后面的高 ...
- BLE 广播数据解析
从上一篇GATT Profile 简介中提到过,BLE 设备工作的第一步就是向外广播数据.广播数据中带有设备相关的信息.本文主要说一下 BLE 的广播中的数据的规范以及广播包的解析. 广播模式 BLE ...
- iOS学习——JSON数据解析(十一)
在之前的<iOS学习——xml数据解析(九)>介绍了xml数据解析,这一篇简单介绍一下Json数据解析.JSON 即 JavaScript Object Natation,它是一种轻量级的 ...
- Python网络爬虫之三种数据解析方式
1. 正则解析 正则例题 import re # string1 = """<div>静夜思 # 窗前明月光 # 疑是地上霜 # 举头望明月 # 低头思故乡 ...
- 05.Python网络爬虫之三种数据解析方式
引入 回顾requests实现数据爬取的流程 指定url 基于requests模块发起请求 获取响应对象中的数据 进行持久化存储 其实,在上述流程中还需要较为重要的一步,就是在持久化存储之前需要进行指 ...
- 初探iOS网络开发,数据解析。
通过大众点评平台开发来简单了解一下,oc的网络编程和数据解析(json) 首先我们需要到大大众点评开发者平台申请一个key.http://developer.dianping.com/app/tech ...
- 数据解析之xpath
一.环境安装 下载lxml pip install lxml 二.使用 XPath 使用路径表达式来选取 XML 文档中的节点或节点集.节点是通过沿着路径 (path) 或者步 (steps) 来选取 ...
随机推荐
- 解决AD域认证问题—“未知的身份验证机制”
场景: Ad认证登录系统,之前正常.不知服务器调了什么,导致无法登录.提示信息如标题. 解决方案: DirectoryEntry adRoot = new DirectoryEntry("L ...
- [git]Git常用命令
转自:http://www.cnblogs.com/idche/archive/2011/07/05/2098165.htmlGIT 学习笔记 集中化的版本控制系统 CVCS(Centralized ...
- (四)java程序基本组成
一个基本的java程序一般包括几个部分,分别是程序所在的包名.程序中用到的其他包的路径.程序的类.类中的方法.变量和字面量. package demo; import java.util.Date; ...
- php没有开启Memcache扩展类时
模拟PHP Memcache 类.当服务器没有开启Memcache扩展的时候.可以采用本类使用方法class_exists('Memcache') or include './Memcache.cla ...
- TCP程序设计
在Java中使用Socket(套接字)完成TCP程序的开发,使用此类可以方便地建立可靠的.双向的.持续的.点对点的通信连接. 在Socket的程序开发中,服务器端使用ServerSoc ...
- Android: 触屏fling/scroll/drag的区别及其详细过程
Google了一下,终于搞清了touch screen下的几种操作模式(对应的是事件). 对于一个view, 常用的操作有点击(click)和长按(long press)二种.实际上,这些操作类型是A ...
- CF534A Exam 构造
An exam for n students will take place in a long and narrow room, so the students will sit in a line ...
- group by 获取总记录数
sql中有group buy 后如何获取总记录的条数,来生成分页 当然一般情况下我是不推荐这样的分页,如果你真的需要应该是你表结构设计有问题 1.适用于所有情况 $db = new PDO(DSN.. ...
- 转--Android实现ListView过滤功能,继承于BaseAdapter,非ArrayAdapter。
其实实现ListView过滤功能最方便的便是使用ArrayAdapter,里面自带的getFilter()方法能很方便的实现此功能,但是在实际的开发中,一般都是继承于BaseAdapter.还有一种是 ...
- setcookie各个参数详解
定义和用法 setcookie() 函数向客户端发送一个 HTTP cookie. cookie 是由服务器发送到浏览器的变量.cookie 通常是服务器嵌入到用户计算机中的小文本文件.每当计算机通过 ...