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();