
PNG是⼀种⾮常流⾏的图⽚格式,它不仅⽀持透明效果,⽽且图⽚数据经过了压缩处理,所以⼴泛⽤于web等应⽤。
PNG的⽂件格式:
PNG⽂件中的数据,总是以⼀个固定的8个字节开头:
除此之外,PNG的其他数据都是以数据块的⽅式组织,它们被分为标准数据块和辅助数据块,其中的辅助数据块是可选的。关键数据块包含我们必须的图⽚信息,我们之后要重点解析的也是关键数据块。
每种数据块的结构:
Length:该数据块的中Chunk Data的长度;
Chunk Type Code:数据类型,就是指上⾯提到的IHDR,IEND等;
Chunk Data:数据区域,如果是IDAT,就表⽰存储的还未解压的图⽚数据;
CRC:循环冗余效验码;
具体实现:(实现中没有处理png数据中变形的情况,部分头中的宏定义来⾃libpng,实例不具备实⽤性,仅作参考)
头⽂件:
1 #ifndef __PNG__
2#define __PNG__
3
4 #include 5 #include 6 #include 7 #include "zlib/zlib.h" 8 9/** 10 * 类型标志 11*/ 12#define PNG_FLAG_HEX "504E470D0A1A0A" 13 14/** 15 * 数据块类型 16*/ 17#define DATA_CHUNK_TYPE_IHDR "IHDR" 18#define DATA_CHUNK_TYPE_IDAT "IDAT" 19#define DATA_CHUNK_TYPE_IEND "IEND" 20#define DATA_CHUNK_TYPE_tEXt "tEXt" 21#define DATA_CHUNK_TYPE_iTXt "iTXt" 22 23/** 24 * 过滤⽅式 25*/ 26#define DATA_FILTER_TYPE_DEFAULT 0 27#define DATA_FILTER_TYPE_ADD_ROW 1 28#define DATA_FILTER_TYPE_ADD_UP 2 29#define DATA_FILTER_TYPE_AVERGE 3 30#define DATA_FILTER_TYPE_PAETH 4 31 32/* color type masks */ 33#define PNG_COLOR_MASK_PALETTE 1 34#define PNG_COLOR_MASK_COLOR 2 35#define PNG_COLOR_MASK_ALPHA 4 36 37/* color types. Note that not all combinations are legal */ 38#define PNG_COLOR_TYPE_GRAY 0 39#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE) 40#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR) 41#define PNG_COLOR_TYPE_RGB_ALPHA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA) 42#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA) 43 44#define RGB_USE_ALPHA(vr, vg, vb, va) \ 45 (unsigned)(((unsigned)((unsigned char)(vr) * ((unsigned char)(va) + 1)) >> 8) | \ 46 ((unsigned)((unsigned char)(vg) * ((unsigned char)(va) + 1) >> 8) << 8) | \ 47 ((unsigned)((unsigned char)(vb) * ((unsigned char)(va) + 1) >> 8) << 16) | \ 48 ((unsigned)(unsigned char)(va) << 24))49 50/** 51 * ⼀次解压图⽚数据的 52*/ 53#define DECOMPRESSION_MAX_BYTES 8192 54 55/** 56 * 数据块信息 57*/ 58 typedef struct _DataChunkHeader 59 { 60// 数据长度 61 unsigned char length[4]; 62// 数据类型 63 unsigned char type[4]; } DataChunkHeader; 65 66/** 67 * IHDR数据 68*/ 69 typedef struct _IDHRData 70 { 71 unsigned char width[4]; 72 unsigned char height[4]; 73 unsigned char bitDepth[1]; 74 unsigned char colorType[1]; 75 unsigned char compressionMethod[1]; 76 unsigned char filterMethod[1]; 77 unsigned char interlaceMethod[1]; 78 } IDHRData; 79 80/** 81 * PNG图⽚类 82*/ 83class PNG 84 { 85public: 86 PNG(); 87 PNG(const char* filePath); 88 ~PNG(); 90 91int getWindth(); 92int getHeight(); 93 94/** 95 * 获取图⽚宽度 96*/ 97 unsigned char* getImageData(); 98 99private: 100int m_width; 101int m_height; 102 103 unsigned char m_bitDepth; 104 unsigned char m_colorType; 105 unsigned char m_compressionMethod; 106 unsigned char m_filterMethod; 107 unsigned char m_interlaceMethod; 108 unsigned char m_chanels; 109 110 unsigned char* m_imageData; 111 112/** 113 * 从⽂件加载图⽚数据 114*/ 115bool loadImageDataFromFile(const char* filePath); 116 117/** 118 * 解析数值 119*/ 120int parseNumber(const unsigned char* data, int len); 121 122/** 123 * 解压数据 124*/ 125int decompressData(z_stream* zStream, unsigned char* data, int dataLen, int leftLen, FILE *pFile); 126 127/** 128 * ⽣成图⽚数据 129*/ 130void generateImageData(unsigned char* data, unsigned long dataLen); 131 132/**133 * 默认的过滤⽅式 134*/ 135void defaultFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 136 137/** 138 * 当前⾏相加的过滤⽅式 139*/ 140void addCurrentRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 141 142/** 143 * 前⼀⾏相加的过滤⽅式 144*/ 145void addUpRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 146 147/** 148 * 平均的过滤⽅式 149*/ 150void avergeFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 151 152/** 153 * paeth的过滤⽅式 154*/ 155void paethFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 156 157/** 158 * 解析IHDR数据 159*/ 160void parseIHDRData(DataChunkHeader& dataChunkHeader, FILE* pFile); 161 162/** 163 * 解析IDAT数据 1*/ 165void parseIDATData(DataChunkHeader& dataChunkHeader, FILE* pFile); 166 167/** 168 * 解析IEND数据 169*/ 170void parseIENDData(DataChunkHeader& dataChunkHeader, FILE *pFile); 171 172/** 173 * 解析其他数据 174*/ 175void parseCommonData(DataChunkHeader& dataChunkHeader, FILE *pFile); 176 }; 177 178#endif cpp⽂件: 1 #include "png.h" 2 #include "utils/cUtil.h" 3 #include 4 5 #include 6 7/** 8 * 默认构造函数 9*/ 10 PNG::PNG() 11 { 12this->m_width = 0; 13this->m_height = 0; 14 15this->m_imageData = 0; 16 } 17 18/** 19 * 构造函数 20 * @param filePath 图⽚路径 21*/ 22 PNG::PNG(const char *filePath) 23 { 24this->m_width = 0; 25this->m_height = 0; 26 27this->loadImageDataFromFile(filePath); 28 } 29 30/** 31 * 析构函数 32*/ 33 PNG::~PNG() 34 { 3537 38/** 39 * 从⽂件加载图⽚数据 40*/ 41bool PNG::loadImageDataFromFile(const char* filePath) 42 { 43 FILE* pFile = fopen(filePath, "rb"); 44if (!pFile) 45return false; 46 47// 解析PNG标志 48char flag[8]; 49char hexFlag[17]; 50 fread(flag, 1, 8, pFile); 51 toHexStr(flag, 8, hexFlag); 52if (strcmp(hexFlag, PNG_FLAG_HEX) != 0) 53return false; 54 55// 解析图⽚数据 56 DataChunkHeader dataChunkHeader; 57char dataChunkHeaderType[5]; 58do { 59 fread(&dataChunkHeader, 1, sizeof(DataChunkHeader), pFile); 60 61 memcpy(dataChunkHeaderType, dataChunkHeader.type, 4); 62 dataChunkHeaderType[4] = '\\0'; 63 // IHDR 65if ( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IHDR) == 0 ) { 66this->parseIHDRData(dataChunkHeader, pFile); 67 } 68// IDAT 69else if ( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IDAT) == 0 ) { 70this->parseIDATData(dataChunkHeader, pFile); 71 } 72// IEND 73else if ( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IEND) == 0 ) { 74this->parseIENDData(dataChunkHeader, pFile); 75 } 76// 其他数据 77else { 78this->parseCommonData(dataChunkHeader, pFile); 79 } 80 } while( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IEND) != 0 ); 81 82int i = 1; 83 84return true; 85 } 86 87/** 88 * 解析数值 */ 90int PNG::parseNumber(const unsigned char* data, int len) 91 { 92char localNum[4]; 93 94bool isLittleEndian = checkEndian(); 95for (int i = 0; i<4; i++) { 96char ch; 97 98if (isLittleEndian) { 99if (i <= len-1) 100 ch = data[len - 1 - i]; 101else 102 ch = '\\0'; 103 } 104else { 105if (i <= len-1) 106 ch = data[i]; 107else 108 ch = '\\0'; 109 } 110 localNum[i] = ch; 111 } 112 113int num; 114 memcpy(&num, localNum, 4); 115return num; 116 } 117 118/** 119 * 解析IHDR数据121void PNG::parseIHDRData(DataChunkHeader& dataChunkHeader, FILE* pFile) 122 { 123int dataLen = this->parseNumber(dataChunkHeader.length, 4); 124 125 IDHRData idhrData; 126char crc[4]; 127 128 fread(&idhrData, 1, sizeof(IDHRData), pFile); 129 fread(crc, 1, 4, pFile); 130 131this->m_width = this->parseNumber(idhrData.width, 4); 132this->m_height = this->parseNumber(idhrData.height, 4); 133this->m_bitDepth = this->parseNumber(idhrData.bitDepth, 1); 134this->m_colorType = this->parseNumber(idhrData.colorType, 1); 135this->m_compressionMethod = this->parseNumber(idhrData.compressionMethod, 1); 136this->m_filterMethod = this->parseNumber(idhrData.filterMethod, 1); 137this->m_interlaceMethod = this->parseNumber(idhrData.interlaceMethod, 1); 138this->m_chanels = 0; 139 140switch (this->m_colorType) { 141case PNG_COLOR_TYPE_GRAY: 142case PNG_COLOR_TYPE_PALETTE: 143this->m_chanels = 1; 144break; 145case PNG_COLOR_TYPE_RGB: 146this->m_chanels = 3; 147break; 148case PNG_COLOR_TYPE_GRAY_ALPHA: 149this->m_chanels = 2; 150break; 151case PNG_COLOR_TYPE_RGB_ALPHA: 152this->m_chanels = 4; 153break; 154default: 155this->m_chanels = 0; 156break; 157 } 158 } 159 160/** 161 * 解压数据 162*/ 163int PNG::decompressData(z_stream* zStream, unsigned char* data, int dataLen, int leftLen, FILE *pFile) 1 { 165int result = 0; 166 167int leftBytesCount = leftLen; 168int avail_out = -1; 169do { 170if (zStream->avail_in == 0) { 171if (avail_out == 0) 172break; 173else { 174if (leftBytesCount == 0) { 175 DataChunkHeader dataChunkHeader; 176 fread(&dataChunkHeader, 1, sizeof(DataChunkHeader), pFile); 177 178int newDataLen = this->parseNumber(dataChunkHeader.length, 4); 179 unsigned char* newData = new unsigned char[dataLen + newDataLen]; 180char crc[4]; 181 182 fread(newData + dataLen, 1, newDataLen, pFile); 183 fread(crc, 1, 4, pFile); 184 memcpy(newData, data, dataLen); 185 186 delete data; 187 data = newData; 188 1 zStream->next_in = newData + dataLen; 190 zStream->avail_in = newDataLen; 191 192 dataLen = dataLen + newDataLen; 193 194return this->decompressData(zStream, data, dataLen, 0, pFile); 195 } 196 } 197 198// 导出数据是否超过 199if (leftBytesCount > DECOMPRESSION_MAX_BYTES) 200 zStream->avail_in = DECOMPRESSION_MAX_BYTES; 201else 202 zStream->avail_in = leftBytesCount; 203204 leftBytesCount -= zStream->avail_in; 205 } 206 207if (avail_out > 0) 208 zStream->avail_out = avail_out; 209else 210 zStream->avail_out = m_width * 4 + 1; 211 212 result = inflate(zStream, Z_NO_FLUSH); 213if (result != Z_OK) 214break; 215 216 avail_out = zStream->avail_out; 217 } while (zStream->avail_in >= 0); 218 219return result; 220 } 221 222/** 223 * ⽣成图⽚数据 224*/ 225void PNG::generateImageData(unsigned char* data, unsigned long dataLen) 226 { 227// ⾏字节数 228int rowBytes = this->m_chanels * this->m_width; 229 230// 初始化图⽚数据 231this->m_imageData = new unsigned char[rowBytes * this->m_height]; 232 233 unsigned char* pImageData = this->m_imageData; 234 unsigned char* pRowData = data; 235 236for (int rowIndex = 0; rowIndex < this->m_height; rowIndex++) { 237// 过滤类型 238 unsigned char filterType = pRowData[0]; 239 240 pRowData += 1; 241 242switch (filterType) { 243// 不需要过滤处理 244case DATA_FILTER_TYPE_DEFAULT: 245this->defaultFilterType(pImageData, pRowData, rowBytes); 246break; 247// 当前⾏相加 248case DATA_FILTER_TYPE_ADD_ROW: 249this->addCurrentRowFilterType(pImageData, pRowData, rowBytes); 250break; 251// 和前⼀⾏相加 252case DATA_FILTER_TYPE_ADD_UP: 253this->addUpRowFilterType(pImageData, pRowData, rowBytes); 254break; 255// 求平均 256case DATA_FILTER_TYPE_AVERGE: 257this->avergeFilterType(pImageData, pRowData, rowBytes); 258break; 259// Paeth 260case DATA_FILTER_TYPE_PAETH: 261this->paethFilterType(pImageData, pRowData, rowBytes); 262break; 263// 类型错误 2default: 265break; 266 } 267 268 pImageData += rowBytes; 269 pRowData += rowBytes; 270 271char text[100]; 272 sprintf(text, "filter type:%d, rowIndex:%d \\n 273 OutputDebugString(text); 274 } 275 276int channel = rowBytes / this->m_width; 277if (channel == 4) { 278 unsigned int *tmp = (unsigned int *)this->m_imageData; 279 280for (unsigned short i = 0; i < this->m_height; i++) { 281for (unsigned int j = 0; j < rowBytes; j+=4) { 282 unsigned int offset = i * rowBytes + j; 283 284 *tmp++ = RGB_USE_ALPHA( 285this->m_imageData[offset], 286this->m_imageData[offset+1], 287this->m_imageData[offset+2], 288this->m_imageData[offset+3] 2 ); 290 } 291 } 292 } 293 } 294 295/** 296 * 默认的过滤⽅式 297*/ 298void PNG::defaultFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 299 { 300for (int i = 0; i < rowBytes; i++) { 301 *pImageData++ = *pRowData++; 302 } 303 } 304 305/** 306 * 当前⾏相加的过滤⽅式 307*/ 308void PNG::addCurrentRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 309 { 310for (int i = 0; i < rowBytes; i++) { 311if (i == 0) { 312 memcpy(pImageData, pRowData, 4); 313 i += 3; 314 pImageData += 4; 315 pRowData += 4; 316 } 317else { 318 *pImageData++ = (unsigned char)(((int)*(pRowData++) + (int)*(pImageData-4)) & 0xFF); 319 } 320 } 321 } 322 323/** 324 * 前⼀⾏相加的过滤⽅式 325*/ 326void PNG::addUpRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 327 { 328for (int i = 0; i < rowBytes; i++) { 329 *pImageData++ = (unsigned char)(((int)*(pRowData++) + (int)*(pImageData-rowBytes)) & 0xFF); 330 } 331 } 332 333/** 334 * 平均的过滤⽅式 335*/ 336void PNG::avergeFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 337 { 338for (int i = 0; i < rowBytes; i++) { 339int averge = 0; 340 341if (i <= 3) { 342 averge = ((int)*(pImageData-rowBytes)) / 2; 343 344 *pImageData++ = (unsigned char)((averge + (int)*(pRowData++)) & 0xFF); 345 } 346else { 347 averge = (((int)*(pImageData-4)) + ((int)*(pImageData-rowBytes))) / 2; 348 349 *pImageData++ = (unsigned char)((averge + (int)*(pRowData++)) & 0xFF); 350 } 351 } 352 } 353 354/** 355 * paeth的过滤⽅式 356*/ 357int Paeth(int a, int b, int c) 358 { 359int p = a + b - c; 360int pa = abs(p - a); 361int pb = abs(p - b); 362int pc = abs(p - c); 363 3int Paeth; 365if(pa <= pb && pa <= pc) 366 Paeth = a; 367else if (pb <= pc) 368 Paeth = b; 369else 370 Paeth = c; 371return Paeth ;372 } 373void PNG::paethFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 374 { 375for (int i = 0; i < rowBytes; i++) { 376if (i <= 3) { 377 *pImageData++ = (unsigned char)(((int)*(pRowData++) + (int)*(pImageData-rowBytes)) & 0xFF); 378 } 379else { 380 unsigned char left = *(pImageData - 4); 381 unsigned char up = *(pImageData - rowBytes); 382 unsigned char leftUp = *(pImageData - rowBytes - 4); 383 384int value = Paeth((int)left, (int)up, (int)leftUp); 385 386 *pImageData++ = (unsigned char)(((int)*(pRowData++) + value) & 0xFF); 387 } 388 } 3 } 390 391/** 392 * 解析IDAT数据 393*/ 394void PNG::parseIDATData(DataChunkHeader& dataChunkHeader, FILE* pFile) 395 { 396// 解压后的图⽚数据 397 unsigned char* imageData = new unsigned char[m_width * m_height * 4]; 398 399int dataLen = this->parseNumber(dataChunkHeader.length, 4); 400// 解压前的图⽚数据 401 unsigned char* data = new unsigned char[dataLen]; 402char crc[4]; 403// 提取数据 404 fread(data, 1, dataLen, pFile); 405 fread(crc, 1, 4, pFile); 406 407// 存放临时的解压数据 408 unsigned long decompressDataLen = m_width * m_height * 4 + m_height; 409 unsigned char* decompressData = new unsigned char[decompressDataLen]; 410 411 z_stream* zStream = new z_stream(); 412 zStream->next_in = data; 413 zStream->next_out = decompressData; 414 415 inflateInit(zStream); 416 417// 解压数据 418this->decompressData(zStream, data, dataLen, dataLen, pFile); 419// ⽣成图⽚数据 420this->generateImageData(decompressData, decompressDataLen); 421 422/* 423 int result = 0; 424 // 开始解压数据 425 int leftBytesCount = dataLen; 426 int avail_out = -1; 427 do { 428 if (zStream->avail_in == 0) { 429 if (avail_out == 0) 430 break; 431 else { 432 if (leftBytesCount == 0) { 433 434 } 435 } 436 437 // 导出数据是否超过 438 if (leftBytesCount > DECOMPRESSION_MAX_BYTES) 439 zStream->avail_in = DECOMPRESSION_MAX_BYTES; 440 else 441 zStream->avail_in = leftBytesCount; 442 443 leftBytesCount = dataLen - zStream->avail_in; 444 } 445 446 if (avail_out > 0) 447 zStream->avail_out = avail_out; 448 else 449 zStream->avail_out = m_width * 4 + 1; 450 451 result = inflate(zStream, Z_NO_FLUSH); 452 if (result != Z_OK) 453 break; 454 455 avail_out = zStream->avail_out;456 } while (zStream->avail_in >= 0); 457 // 数据解压是否成功 458 if (result == Z_STREAM_END) { 459 int i = 1; 460 } 461*/ 462 } 463 4/** 465 * 解析IEND数据 466*/ 467void PNG::parseIENDData(DataChunkHeader& dataChunkHeader, FILE *pFile) 468 { 469char crc[4]; 470 fread(crc, 1, 4, pFile); 471 } 472 473/** 474 * 解析其他数据 475*/ 476void PNG::parseCommonData(DataChunkHeader& dataChunkHeader, FILE *pFile) 477 { 478int dataLen = this->parseNumber(dataChunkHeader.length, 4); 479 fseek(pFile, dataLen + 4, SEEK_CUR); 480 } 481 482/** 483 * 获取图⽚宽度 484*/ 485 unsigned char* PNG::getImageData() 486 { 487return this->m_imageData; 488 } 4 490/** 491 * 获取图⽚宽度 492*/ 493int PNG::getWindth() 494 { 495return this->m_width; 496 } 497 498/** 499 * 获取图⽚⾼度 500*/ 501int PNG::getHeight() 502 { 503return this->m_height; 504 } 如果需要绘制图⽚,可以使⽤opengl库 参考代码: 1 glViewport(0, 0, winWidth, winHeight); 2 3 glMatrixMode(GL_PROJECTION); 4 glLoadIdentity(); 5 glOrtho(0.0f, winWidth - 1.0, 0.0, winHeight - 1.0, -10.0, 10.0); 6 7 glMatrixMode(GL_MODELVIEW); 8 glLoadIdentity(); 9 10 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 11 12 glEnable(GL_TEXTURE_2D); 13 14int width = png->getWindth(); 15int height = png->getHeight(); 16 unsigned char* data = png->getImageData(); 17 18 GLuint name1; 19 glGenTextures(1, &name1); 20 glBindTexture(GL_TEXTURE_2D, name1); 21 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 22 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 23 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 24 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 25 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,GL_RGBA, GL_UNSIGNED_BYTE, data); 26 glBegin(GL_POLYGON); 27 glTexCoord2f(1, 1); 28 glVertex3d(800, 800, 2); 29 glTexCoord2f(0, 1);30 glVertex3d(0, 800, 2); 31 glTexCoord2f(0, 0); 32 glVertex3d(0, 0, 2); 33 glTexCoord2f(1, 0); 34 glVertex3d(800, 0, 2); 35 glEnd();
