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,实例不具备实用性,仅作参考)
头文件:
1 #ifndef __PNG__ 2 #define __PNG__ 3 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <math.h> 7 #include "zlib/zlib.h" 8 9 /** 10 * 类型标志 11 */ 12 #define PNG_FLAG_HEX "89504E470D0A1A0A" 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]; 64 } 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 */ 83 class PNG 84 { 85 public: 86 PNG(); 87 PNG(const char* filePath); 88 89 ~PNG(); 90 91 int getWindth(); 92 int getHeight(); 93 94 /** 95 * 获取图片宽度 96 */ 97 unsigned char* getImageData(); 98 99 private: 100 int m_width; 101 int 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 */ 115 bool loadImageDataFromFile(const char* filePath); 116 117 /** 118 * 解析数值 119 */ 120 int parseNumber(const unsigned char* data, int len); 121 122 /** 123 * 解压数据 124 */ 125 int decompressData(z_stream* zStream, unsigned char* data, int dataLen, int leftLen, FILE *pFile); 126 127 /** 128 * 生成图片数据 129 */ 130 void generateImageData(unsigned char* data, unsigned long dataLen); 131 132 /** 133 * 默认的过滤方式 134 */ 135 void defaultFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 136 137 /** 138 * 当前行相加的过滤方式 139 */ 140 void addCurrentRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 141 142 /** 143 * 前一行相加的过滤方式 144 */ 145 void addUpRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 146 147 /** 148 * 平均的过滤方式 149 */ 150 void avergeFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 151 152 /** 153 * paeth的过滤方式 154 */ 155 void paethFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes); 156 157 /** 158 * 解析IHDR数据 159 */ 160 void parseIHDRData(DataChunkHeader& dataChunkHeader, FILE* pFile); 161 162 /** 163 * 解析IDAT数据 164 */ 165 void parseIDATData(DataChunkHeader& dataChunkHeader, FILE* pFile); 166 167 /** 168 * 解析IEND数据 169 */ 170 void parseIENDData(DataChunkHeader& dataChunkHeader, FILE *pFile); 171 172 /** 173 * 解析其他数据 174 */ 175 void parseCommonData(DataChunkHeader& dataChunkHeader, FILE *pFile); 176 }; 177 178 #endif
cpp文件:
1 #include "png.h" 2 #include "utils/cUtil.h" 3 #include <stdlib.h> 4 5 #include <windows.h> 6 7 /** 8 * 默认构造函数 9 */ 10 PNG::PNG() 11 { 12 this->m_width = 0; 13 this->m_height = 0; 14 15 this->m_imageData = 0; 16 } 17 18 /** 19 * 构造函数 20 * @param filePath 图片路径 21 */ 22 PNG::PNG(const char *filePath) 23 { 24 this->m_width = 0; 25 this->m_height = 0; 26 27 this->loadImageDataFromFile(filePath); 28 } 29 30 /** 31 * 析构函数 32 */ 33 PNG::~PNG() 34 { 35 36 } 37 38 /** 39 * 从文件加载图片数据 40 */ 41 bool PNG::loadImageDataFromFile(const char* filePath) 42 { 43 FILE* pFile = fopen(filePath, "rb"); 44 if (!pFile) 45 return false; 46 47 // 解析PNG标志 48 char flag[8]; 49 char hexFlag[17]; 50 fread(flag, 1, 8, pFile); 51 toHexStr(flag, 8, hexFlag); 52 if (strcmp(hexFlag, PNG_FLAG_HEX) != 0) 53 return false; 54 55 // 解析图片数据 56 DataChunkHeader dataChunkHeader; 57 char dataChunkHeaderType[5]; 58 do { 59 fread(&dataChunkHeader, 1, sizeof(DataChunkHeader), pFile); 60 61 memcpy(dataChunkHeaderType, dataChunkHeader.type, 4); 62 dataChunkHeaderType[4] = '\0'; 63 64 // IHDR 65 if ( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IHDR) == 0 ) { 66 this->parseIHDRData(dataChunkHeader, pFile); 67 } 68 // IDAT 69 else if ( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IDAT) == 0 ) { 70 this->parseIDATData(dataChunkHeader, pFile); 71 } 72 // IEND 73 else if ( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IEND) == 0 ) { 74 this->parseIENDData(dataChunkHeader, pFile); 75 } 76 // 其他数据 77 else { 78 this->parseCommonData(dataChunkHeader, pFile); 79 } 80 } while( strcmp(dataChunkHeaderType, DATA_CHUNK_TYPE_IEND) != 0 ); 81 82 int i = 1; 83 84 return true; 85 } 86 87 /** 88 * 解析数值 89 */ 90 int PNG::parseNumber(const unsigned char* data, int len) 91 { 92 char localNum[4]; 93 94 bool isLittleEndian = checkEndian(); 95 for (int i = 0; i<4; i++) { 96 char ch; 97 98 if (isLittleEndian) { 99 if (i <= len-1) 100 ch = data[len - 1 - i]; 101 else 102 ch = '\0'; 103 } 104 else { 105 if (i <= len-1) 106 ch = data[i]; 107 else 108 ch = '\0'; 109 } 110 localNum[i] = ch; 111 } 112 113 int num; 114 memcpy(&num, localNum, 4); 115 return num; 116 } 117 118 /** 119 * 解析IHDR数据 120 */ 121 void PNG::parseIHDRData(DataChunkHeader& dataChunkHeader, FILE* pFile) 122 { 123 int dataLen = this->parseNumber(dataChunkHeader.length, 4); 124 125 IDHRData idhrData; 126 char crc[4]; 127 128 fread(&idhrData, 1, sizeof(IDHRData), pFile); 129 fread(crc, 1, 4, pFile); 130 131 this->m_width = this->parseNumber(idhrData.width, 4); 132 this->m_height = this->parseNumber(idhrData.height, 4); 133 this->m_bitDepth = this->parseNumber(idhrData.bitDepth, 1); 134 this->m_colorType = this->parseNumber(idhrData.colorType, 1); 135 this->m_compressionMethod = this->parseNumber(idhrData.compressionMethod, 1); 136 this->m_filterMethod = this->parseNumber(idhrData.filterMethod, 1); 137 this->m_interlaceMethod = this->parseNumber(idhrData.interlaceMethod, 1); 138 this->m_chanels = 0; 139 140 switch (this->m_colorType) { 141 case PNG_COLOR_TYPE_GRAY: 142 case PNG_COLOR_TYPE_PALETTE: 143 this->m_chanels = 1; 144 break; 145 case PNG_COLOR_TYPE_RGB: 146 this->m_chanels = 3; 147 break; 148 case PNG_COLOR_TYPE_GRAY_ALPHA: 149 this->m_chanels = 2; 150 break; 151 case PNG_COLOR_TYPE_RGB_ALPHA: 152 this->m_chanels = 4; 153 break; 154 default: 155 this->m_chanels = 0; 156 break; 157 } 158 } 159 160 /** 161 * 解压数据 162 */ 163 int PNG::decompressData(z_stream* zStream, unsigned char* data, int dataLen, int leftLen, FILE *pFile) 164 { 165 int result = 0; 166 167 int leftBytesCount = leftLen; 168 int avail_out = -1; 169 do { 170 if (zStream->avail_in == 0) { 171 if (avail_out == 0) 172 break; 173 else { 174 if (leftBytesCount == 0) { 175 DataChunkHeader dataChunkHeader; 176 fread(&dataChunkHeader, 1, sizeof(DataChunkHeader), pFile); 177 178 int newDataLen = this->parseNumber(dataChunkHeader.length, 4); 179 unsigned char* newData = new unsigned char[dataLen + newDataLen]; 180 char 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 189 zStream->next_in = newData + dataLen; 190 zStream->avail_in = newDataLen; 191 192 dataLen = dataLen + newDataLen; 193 194 return this->decompressData(zStream, data, dataLen, 0, pFile); 195 } 196 } 197 198 // 导出数据是否超过限制 199 if (leftBytesCount > DECOMPRESSION_MAX_BYTES) 200 zStream->avail_in = DECOMPRESSION_MAX_BYTES; 201 else 202 zStream->avail_in = leftBytesCount; 203 204 leftBytesCount -= zStream->avail_in; 205 } 206 207 if (avail_out > 0) 208 zStream->avail_out = avail_out; 209 else 210 zStream->avail_out = m_width * 4 + 1; 211 212 result = inflate(zStream, Z_NO_FLUSH); 213 if (result != Z_OK) 214 break; 215 216 avail_out = zStream->avail_out; 217 } while (zStream->avail_in >= 0); 218 219 return result; 220 } 221 222 /** 223 * 生成图片数据 224 */ 225 void PNG::generateImageData(unsigned char* data, unsigned long dataLen) 226 { 227 // 行字节数 228 int rowBytes = this->m_chanels * this->m_width; 229 230 // 初始化图片数据 231 this->m_imageData = new unsigned char[rowBytes * this->m_height]; 232 233 unsigned char* pImageData = this->m_imageData; 234 unsigned char* pRowData = data; 235 236 for (int rowIndex = 0; rowIndex < this->m_height; rowIndex++) { 237 // 过滤类型 238 unsigned char filterType = pRowData[0]; 239 240 pRowData += 1; 241 242 switch (filterType) { 243 // 不需要过滤处理 244 case DATA_FILTER_TYPE_DEFAULT: 245 this->defaultFilterType(pImageData, pRowData, rowBytes); 246 break; 247 // 当前行相加 248 case DATA_FILTER_TYPE_ADD_ROW: 249 this->addCurrentRowFilterType(pImageData, pRowData, rowBytes); 250 break; 251 // 和前一行相加 252 case DATA_FILTER_TYPE_ADD_UP: 253 this->addUpRowFilterType(pImageData, pRowData, rowBytes); 254 break; 255 // 求平均 256 case DATA_FILTER_TYPE_AVERGE: 257 this->avergeFilterType(pImageData, pRowData, rowBytes); 258 break; 259 // Paeth 260 case DATA_FILTER_TYPE_PAETH: 261 this->paethFilterType(pImageData, pRowData, rowBytes); 262 break; 263 // 类型错误 264 default: 265 break; 266 } 267 268 pImageData += rowBytes; 269 pRowData += rowBytes; 270 271 char text[100]; 272 sprintf(text, "filter type:%d, rowIndex:%d \n", filterType, rowIndex); 273 OutputDebugString(text); 274 } 275 276 int channel = rowBytes / this->m_width; 277 if (channel == 4) { 278 unsigned int *tmp = (unsigned int *)this->m_imageData; 279 280 for (unsigned short i = 0; i < this->m_height; i++) { 281 for (unsigned int j = 0; j < rowBytes; j+=4) { 282 unsigned int offset = i * rowBytes + j; 283 284 *tmp++ = RGB_USE_ALPHA( 285 this->m_imageData[offset], 286 this->m_imageData[offset+1], 287 this->m_imageData[offset+2], 288 this->m_imageData[offset+3] 289 ); 290 } 291 } 292 } 293 } 294 295 /** 296 * 默认的过滤方式 297 */ 298 void PNG::defaultFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 299 { 300 for (int i = 0; i < rowBytes; i++) { 301 *pImageData++ = *pRowData++; 302 } 303 } 304 305 /** 306 * 当前行相加的过滤方式 307 */ 308 void PNG::addCurrentRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 309 { 310 for (int i = 0; i < rowBytes; i++) { 311 if (i == 0) { 312 memcpy(pImageData, pRowData, 4); 313 i += 3; 314 pImageData += 4; 315 pRowData += 4; 316 } 317 else { 318 *pImageData++ = (unsigned char)(((int)*(pRowData++) + (int)*(pImageData-4)) & 0xFF); 319 } 320 } 321 } 322 323 /** 324 * 前一行相加的过滤方式 325 */ 326 void PNG::addUpRowFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 327 { 328 for (int i = 0; i < rowBytes; i++) { 329 *pImageData++ = (unsigned char)(((int)*(pRowData++) + (int)*(pImageData-rowBytes)) & 0xFF); 330 } 331 } 332 333 /** 334 * 平均的过滤方式 335 */ 336 void PNG::avergeFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 337 { 338 for (int i = 0; i < rowBytes; i++) { 339 int averge = 0; 340 341 if (i <= 3) { 342 averge = ((int)*(pImageData-rowBytes)) / 2; 343 344 *pImageData++ = (unsigned char)((averge + (int)*(pRowData++)) & 0xFF); 345 } 346 else { 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 */ 357 int Paeth(int a, int b, int c) 358 { 359 int p = a + b - c; 360 int pa = abs(p - a); 361 int pb = abs(p - b); 362 int pc = abs(p - c); 363 364 int Paeth; 365 if(pa <= pb && pa <= pc) 366 Paeth = a; 367 else if (pb <= pc) 368 Paeth = b; 369 else 370 Paeth = c; 371 return Paeth ; 372 } 373 void PNG::paethFilterType(unsigned char* pImageData, unsigned char* pRowData, int rowBytes) 374 { 375 for (int i = 0; i < rowBytes; i++) { 376 if (i <= 3) { 377 *pImageData++ = (unsigned char)(((int)*(pRowData++) + (int)*(pImageData-rowBytes)) & 0xFF); 378 } 379 else { 380 unsigned char left = *(pImageData - 4); 381 unsigned char up = *(pImageData - rowBytes); 382 unsigned char leftUp = *(pImageData - rowBytes - 4); 383 384 int value = Paeth((int)left, (int)up, (int)leftUp); 385 386 *pImageData++ = (unsigned char)(((int)*(pRowData++) + value) & 0xFF); 387 } 388 } 389 } 390 391 /** 392 * 解析IDAT数据 393 */ 394 void PNG::parseIDATData(DataChunkHeader& dataChunkHeader, FILE* pFile) 395 { 396 // 解压后的图片数据 397 unsigned char* imageData = new unsigned char[m_width * m_height * 4]; 398 399 int dataLen = this->parseNumber(dataChunkHeader.length, 4); 400 // 解压前的图片数据 401 unsigned char* data = new unsigned char[dataLen]; 402 char 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 // 解压数据 418 this->decompressData(zStream, data, dataLen, dataLen, pFile); 419 // 生成图片数据 420 this->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 464 /** 465 * 解析IEND数据 466 */ 467 void PNG::parseIENDData(DataChunkHeader& dataChunkHeader, FILE *pFile) 468 { 469 char crc[4]; 470 fread(crc, 1, 4, pFile); 471 } 472 473 /** 474 * 解析其他数据 475 */ 476 void PNG::parseCommonData(DataChunkHeader& dataChunkHeader, FILE *pFile) 477 { 478 int dataLen = this->parseNumber(dataChunkHeader.length, 4); 479 fseek(pFile, dataLen + 4, SEEK_CUR); 480 } 481 482 /** 483 * 获取图片宽度 484 */ 485 unsigned char* PNG::getImageData() 486 { 487 return this->m_imageData; 488 } 489 490 /** 491 * 获取图片宽度 492 */ 493 int PNG::getWindth() 494 { 495 return this->m_width; 496 } 497 498 /** 499 * 获取图片高度 500 */ 501 int PNG::getHeight() 502 { 503 return 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 14 int width = png->getWindth(); 15 int 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();