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