【pnglib】解析png格式的图像
解析png格式的圖像
前言
鑒于有人收藏我07年寫(xiě)的博客,是關(guān)于解析png格式圖像的那一篇。所以我打算再寫(xiě)一篇防止坑你們……
1.圖像的內(nèi)存表示
反復(fù)說(shuō)到計(jì)算機(jī)中只有二進(jìn)制表示,那圖像如何以二進(jìn)制表示呢?現(xiàn)在常用的規(guī)范是用4個(gè)字節(jié)表示顏色,分為4個(gè)通道ARGB,分別為透明度、紅色、綠色、藍(lán)色,這是比較常用的顏色表示方式。還有其他的色彩模型,例如HSV色彩模型,分別為色度、飽和度、純度。還有HLS色彩模型,為色度、亮度、飽和度。
在繪圖庫(kù)和顯示器等層面,能夠直接處理的應(yīng)該是RGB顏色模型。也就是通過(guò)3個(gè)變量控制最終的顏色(加上透明度是4個(gè)),而在顯示器上則是1個(gè)像素包含3種顏色的燈,控制強(qiáng)度從而呈現(xiàn)不同的顏色(或許實(shí)現(xiàn)還有更多細(xì)節(jié),也可能是不同的方法)。小時(shí)候如果你用眼睛貼到電視上,應(yīng)該能夠發(fā)現(xiàn)許多顏色不同的小條。以前低分辨率的手機(jī)屏幕也能發(fā)現(xiàn)類(lèi)型的情形,現(xiàn)在之所以看不出來(lái)是因?yàn)橄袼攸c(diǎn)越來(lái)越小,不太容易看出來(lái)(人通過(guò)3種不同的視錐細(xì)胞感覺(jué)顏色)。真實(shí)世界的顏色是原子那么小的,漸變也更加平滑,而顯示器的顏色實(shí)際是很大一顆一顆的,所以看顯示器和看真實(shí)世界,背后的區(qū)別很大。
?一般行業(yè)內(nèi)都用十六進(jìn)制來(lái)表示顏色,前面說(shuō)了4個(gè)字節(jié),1個(gè)字節(jié)表示1個(gè)通道。那么1個(gè)字節(jié)可表示256個(gè)值,用十六進(jìn)制表示就需要2位。而十六進(jìn)制用0123456789abcdef十六個(gè)符號(hào)表示,所以2位十六進(jìn)制表示范圍為[00, ff],那么一個(gè)顏色就需要8位十六進(jìn)制表示,在代碼中一般加前綴0x代表此數(shù)字是十六進(jìn)制。所以顏色可以表示如下:
| 值 | A | R | G | B | 顏色 |
| 0xff000000 | 0xff | 0x00 | 0x00 | 0x00 | 不透明黑色 |
| 0xff00ff00 | 0xff | 0x00 | 0xff | 0x00 | 不透明綠色 |
| 0x66ff00ff | 0x66 | 0xff | 0x00 | 0xff | 半透明紫色(紅配藍(lán)為紫) |
| 0x00ffffff | 0x00 | 0xff | 0xff | 0xff | 全透明白色 |
在各種軟件中,通過(guò)拾色器也能看到顏色代碼。如果用十進(jìn)制表示,則每個(gè)分量的范圍為[0-255]。用整個(gè)十六進(jìn)制數(shù)字表示即可以是6位,如果包含透明通道就是8位,如下圖所示:
2.PNG格式
此時(shí)我們可以計(jì)算一下120*120像素的圖像需要多大的空間保存,首先一個(gè)像素占4個(gè)字節(jié),那么14400個(gè)像素就是占57600字節(jié),也就是56.25kb。而一個(gè)png格式的圖像,120*120的大小實(shí)際上,大概占幾kb到二十幾kb。
PNG格式全稱(chēng)Portable Network Graphics,中文翻譯為便攜式網(wǎng)絡(luò)圖形,是一種無(wú)損壓縮位圖格式。在不損失圖像顏色信息的情況下,壓縮保存圖像數(shù)據(jù)。由于能夠減小圖像保存的大小,所以它才有存在的價(jià)值。相比JPEG格式,它是無(wú)損的,且?guī)в型该魍ǖ?#xff0c;并且是可免費(fèi)商用的,所以適用于游戲制作,但體積比jpeg略大。
關(guān)于PNG格式更詳細(xì)的數(shù)據(jù)格式,此處就不展開(kāi)了,只說(shuō)怎么用libpng庫(kù)來(lái)讀取和保存png格式圖像。
3.libpng庫(kù)
libpng庫(kù)依賴(lài)zlib庫(kù),所以需要二者都配置,具體怎么配置,可以參考我的其他文章(不過(guò)我現(xiàn)在還沒(méi)寫(xiě)),也可以參考網(wǎng)上的教程。
4.讀取過(guò)程
首先包含頭文件:
#include <png> #include <stdio.h>//這里使用C語(yǔ)言的讀寫(xiě)文件先以標(biāo)準(zhǔn)的讀文件方式,讀取圖像文件:
FILE* fp = NULL; _wfopen_s(&fp, "test.png", L"rb");接著讀取8個(gè)字節(jié)確認(rèn)是不是png格式:
//判斷是否為 png 文件size_t number = 8;png_bytep header = new png_byte[number];fread(header, 1, number, fp);bool is_png = !png_sig_cmp(header, 0, number);if (!is_png){fclose(fp);//必須是png文件return NULL;}創(chuàng)建必要的結(jié)構(gòu):
//創(chuàng)建read struct png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL, NULL, NULL);if (!png_ptr){fclose(fp);return NULL;}//創(chuàng)建圖像信息 infopng_infop info_ptr = png_create_info_struct(png_ptr);if (!info_ptr){fclose(fp);return NULL;}//錯(cuò)誤處理if (setjmp(png_jmpbuf(png_ptr))){fclose(fp);//失敗,則釋放前面創(chuàng)建的結(jié)構(gòu),防止內(nèi)存泄漏png_destroy_read_struct(&png_ptr, &info_ptr, NULL);return NULL;}再進(jìn)行以下步驟,完成后可以關(guān)閉文件句柄:
//設(shè)置數(shù)據(jù)源png_init_io(png_ptr, fp);//表明文件頭已處理png_set_sig_bytes(png_ptr, number);//讀png 這一步會(huì)實(shí)際分配內(nèi)存png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);fclose(fp);從info結(jié)構(gòu),可以獲取圖像的一些信息,其中color_type可以知道是否包含透明通道數(shù)據(jù),之后處理需要用到它:
//從info查詢數(shù)據(jù)unsigned w = png_get_image_width(png_ptr, info_ptr); //獲得圖片寬度unsigned h = png_get_image_height(png_ptr, info_ptr); //獲得圖片高度int color_type = png_get_color_type(png_ptr, info_ptr); //獲得圖片顏色類(lèi)型接下來(lái)就是讀取過(guò)程:
//分配內(nèi)存保存從png解析出的數(shù)據(jù)img->_buffer = new DWORD[w*h];//從info 復(fù)制到 imagepng_bytep *row_point = NULL;row_point = png_get_rows(png_ptr, info_ptr);int block_size = (color_type == 6 ? 4 : 3);//(A)RGBunsigned pos = 0;for (unsigned x = 0; x < h; ++x)for (unsigned y = 0; y < w*block_size; y += block_size){((unsigned char*)img->_buffer)[pos + 0] = row_point[x][y + 2];//b;((unsigned char*)img->_buffer)[pos + 1] = row_point[x][y + 1];//g((unsigned char*)img->_buffer)[pos + 2] = row_point[x][y + 0];//rif (color_type == 6)((unsigned char*)img->_buffer)[pos + 3] = row_point[x][y + 3];//aelse((unsigned char*)img->_buffer)[pos + 3] = 0xff;//沒(méi)有透明通道數(shù)據(jù),則填充不透明0xffpos += 4;}//釋放png內(nèi)存png_destroy_read_struct(&png_ptr, &info_ptr, NULL);?5.保存到文件
FILE* fp;png_infop info_ptr;//png_colorpfopen_s(&fp, L"test.png", "wb");if (fp == NULL){//創(chuàng)建文件失敗!return;}//初始化pnglib static png_structp png_ptr = NULL;if (!png_ptr)png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL, NULL, NULL);if (!png_ptr){return;}info_ptr = png_create_info_struct(png_ptr);if (info_ptr == NULL){fclose(fp);return;}//錯(cuò)誤處理if (setjmp(png_jmpbuf(png_ptr))){fclose(fp);png_destroy_write_struct(&png_ptr, (png_infopp)NULL);return;}unsigned bit_depth = 8;unsigned pixel_byte = 4;unsigned row_byte = _size.w * pixel_byte;//設(shè)置輸出控制png_init_io(png_ptr, fp);//設(shè)置圖像屬性png_set_IHDR(png_ptr, info_ptr, _size.w, _size.h, bit_depth,PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, //交錯(cuò)無(wú)PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);//寫(xiě)頭部png_write_info(png_ptr, info_ptr);//獲取行指針png_bytepp row_pointers = (png_bytep*)malloc(_size.h*sizeof(png_bytep));for (unsigned x = 0; x < _size.h; ++x){//分配一行row_pointers[x] = (png_bytep)malloc(row_byte);for (unsigned y = 0; y < row_byte; y += pixel_byte){row_pointers[x][y + 2] = ((unsigned char*)_buffer)[x * row_byte + y + 0];row_pointers[x][y + 1] = ((unsigned char*)_buffer)[x * row_byte + y + 1];row_pointers[x][y + 0] = ((unsigned char*)_buffer)[x * row_byte + y + 2];row_pointers[x][y + 3] = ((unsigned char*)_buffer)[x * row_byte + y + 3];/*row_pointers[x][y + 2] =row_pointers[x][y + 1] =row_pointers[x][y + 0] =row_pointers[x][y + 3] = 0xff;*/}}//寫(xiě)入全部png_write_image(png_ptr, row_pointers);//寫(xiě)尾部png_write_end(png_ptr, info_ptr);//釋放png內(nèi)存png_destroy_write_struct(&png_ptr, (png_infopp) NULL);/* delete[] row_pointers;delete[] image;*/for (unsigned x = 0; x < _size.h; ++x){//釋放每行free(row_pointers[x]);}free(row_pointers);fclose(fp);結(jié)語(yǔ)
代碼如上,短小精悍。其中錯(cuò)誤處理,以及讀取的數(shù)據(jù)處理,就需要自己看情況修改了。
?
總結(jié)
以上是生活随笔為你收集整理的【pnglib】解析png格式的图像的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【C++教程】03.第一个程序解析
- 下一篇: 【XAuido2】播放wav和ogg格式