首页 > 代码库 > PNG图片数据解析
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,实例不具备实用性,仅作参考)
头文件:
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 = http://www.mamicode.com/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 else102 ch = ‘\0‘;103 }104 else {105 if (i <= len-1)106 ch = data[i];107 else108 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 = http://www.mamicode.com/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 =http://www.mamicode.com/ 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 else202 zStream->avail_in = leftBytesCount;203 204 leftBytesCount -= zStream->avail_in;205 }206 207 if (avail_out > 0)208 zStream->avail_out = avail_out;209 else210 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 = http://www.mamicode.com/new unsigned char[rowBytes * this->m_height];232 233 unsigned char* pImageData = http://www.mamicode.com/this->m_imageData;234 unsigned char* pRowData =http://www.mamicode.com/ 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 // Paeth260 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 = http://www.mamicode.com/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 = http://www.mamicode.com/new unsigned char[m_width * m_height * 4];398 399 int dataLen = this->parseNumber(dataChunkHeader.length, 4);400 // 解压前的图片数据401 unsigned char* data = http://www.mamicode.com/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 = http://www.mamicode.com/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 else441 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 else449 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 = http://www.mamicode.com/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();
PNG图片数据解析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。