首页 > 代码库 > 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图片数据解析