4.使用pnglib读写png图片
??4.使用pnglib讀寫png圖片
? 本章前言:
??? 這章就是為了讀取png格式圖像到32位位圖(ARGB)中,逆之則然,廢話少說開始正題。
? 目標要點總結:
1.? 使用pnglib讀寫文件
? 最終效果:
??? 以下操作就能實現加載png圖像到32位圖像中:
??? Image*img_bg=Image::Create(L"data/img/bg.png");
??? 以下操作就能實現導出png圖像:
??? img_bg->Save_To_PNG(L"bin/bg.png");
? 前題簡要:
??? Image類用一個DWORD一維數組儲存每個點,每個點由ARGB組成,例如0x66ff0000是半透明紅色。假設要訪問點(x,y),應該使用下標索引[y *w + x]。Image類還有圖像的大小信息。
? 具體實現:
??? 首先需要配置pnglib,其中還包括zlib。下載相應源碼后,生成靜態鏈接庫鏈接到
項目。在代碼中解析圖片的cpp包含png.h。下面是解析png圖片的具體實現,可結合注釋理解。
??? Image* Image::Create(constString&path)
??? {
??????? //定義空圖像
??????? Image* img =newImage;
??????? System_imp* sys =System_imp::Get_Instance();
??????? //String 轉 char字符數組
?
?
??????? char temp[512] ={NULL };
??????? path.Get_Multi_Byte_Str(temp, 512);
??????????? ///從文件加載/
??????????? //打開png文件
??????????? FILE* fp = NULL;
??????????? fopen_s(&fp,temp,"rb");
??????????? if (!fp)
??????????? {
??????????????? Debug::Instance()->Write_Line(String(L"文件打開失敗:")+path);
??????????????? assert(0 &&L"Image加載時文件打開失敗!");
??????????????? return NULL;
??????????? }
??????????? //判斷是否為 png文件(用fread讀取8字節,然后調用png_sig_cmp判斷)
??????????? 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);
??????????????? Debug::Instance()->Write_Line(String(L"只支持png格式的圖像讀取:")+path);
??????????????? assert(0 &&L"Image加載時為非png格式圖像!");
??????????????? return NULL;
??????????? }
??????????? //初始化pnglib
??????????? static png_structppng_ptr =NULL;
??????????? if (!png_ptr)
??????????????? png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
??????????????????? NULL, NULL, NULL);
??????????? if (!png_ptr)
??????????? {
??????????????? fclose(fp);
??????????????? assert(0 &&L"Image加載時初始化pnglib失敗!");
??????????????? return NULL;
??????????? }
??????????? //創建圖像信息 info
??????????? png_infop info_ptr = png_create_info_struct(png_ptr);
??????????? if (!info_ptr)
??????????? {
??????????????? fclose(fp);
??????????????? assert(0 &&L"Image加載時創建png_info失敗!");
??????????????? return NULL;
??????????? }
??????????? //錯誤處理
??????????? if (setjmp(png_jmpbuf(png_ptr)))
??????????? {
??????????????? fclose(fp);
??????????????? png_destroy_read_struct(&png_ptr, &info_ptr,png_infopp_NULL);
??????????????? assert(0 &&L"Image加載時 pnglib出現錯誤!");
??????????????? return NULL;
??????????? }
??????????? //設置數據源
??????????? png_init_io(png_ptr,fp);
??????????? //表明文件頭已處理
??????????? png_set_sig_bytes(png_ptr, 8);
??????????? //讀png
??????????? png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND, 0);
??????????? //從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);???????//獲得圖片顏色
??????????? //賦值給image
??????????? img->m_size.w =w;
??????????? img->m_size.h =h;
??????????? img->m_buffer =newDWORD[w*h];
??????????? //從info復制到 image
??????????? png_bytep *row_point =NULL;
??????????? row_point = png_get_rows(png_ptr,info_ptr);
??????????? //帶透明通道一個點是4字節,否則為3字節
??????????? int block_size = (color_type == 6 ? 4 : 3);
??????????? //(A)RGB
??????????? unsigned pos = 0;
??????????? for (unsignedx = 0;x < h; ++x)
??????????????? for (unsignedy = 0;y < w*block_size;y +=block_size)
??????????????? {
??????????????????? ((unsigned char*)img->m_buffer)[pos + 0] =row_point[x][y + 2];//b;
??????????????????? ((unsigned char*)img->m_buffer)[pos + 1] =row_point[x][y + 1];//g
??????????????????? ((unsigned char*)img->m_buffer)[pos + 2] =row_point[x][y + 0];//r
??????????????????? if(color_type == 6)//不帶透明通道就填充 0xff表示不透明
??????????????????????? ((unsigned char*)img->m_buffer)[pos + 3] =row_point[x][y + 3];//a
??????????????????? else
??????????????????????? ((unsigned char*)img->m_buffer)[pos + 3] = 0xff;???????????????????pos += 4;
??????????????? }
??????????? //關閉文件
??????????? fclose(fp);
??????????? //釋放png內存
??????????? png_destroy_read_struct(&png_ptr, &info_ptr,png_infopp_NULL);
??????????? return img;
??? }
?
??? 保存到png圖像,如下調用:
canvas->Get_Image()->Save_To_PNG(L"bin/layer_image.png");
??? 上面的語句將畫布(一個DrawCall)的圖像導出到指定文件中。畫布Canvas會拼接Sprite使用的Image成大圖,從下面的圖片可以看到拼接并導出后的效果(這是導出的layer_image.png的一部分,不是程序截圖哈!):
? 保存圖像數據到PNG的實現如下:
??? void Image::Save_To_PNG(constString&path)
??? {
??????? FILE* fp;
??????? png_infop info_ptr;
???????
??????? char cpath[MAX_PATH] = {NULL };
??????? path.Get_Multi_Byte_Str(cpath,MAX_PATH);
??????? //創建或覆蓋文件
??????? fopen_s(&fp,cpath,"wb");
??????? if (fp ==NULL)
??????? {
??????????? assert(0 && L"Save_To_PNG 創建文件失敗!");
??????????? return;
??????? }
??????? //初始化pnglib(注意相關的函數名read都變成了write)
??????? static png_structppng_ptr =NULL;
??????? if (!png_ptr)
??????????? png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
??????????????? NULL, NULL,NULL);
??????? if (!png_ptr)
??????? {
??????????? assert(0 && L"Save_To_PNG 創建文件時初始化pnglib失敗!");
??????????? return;
??????? }
??????? info_ptr = png_create_info_struct(png_ptr);
??????? if (info_ptr ==NULL)
??????? {
??????????? fclose(fp);
??????????? assert(0 && L"Save_To_PNG:png_create_info_struct失敗!");
??????????? return;
??????? }
??????? //錯誤處理
??????? if (setjmp(png_jmpbuf(png_ptr)))
??????? {
??????????? fclose(fp);
??????????? png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
??????????? assert(0 && L"Save_To_PNG:pnglib出現錯誤!");
??????????? return;
??????? }
??????? //顏色深度8,像素大小(ARGB 4字節),行字節寬度
??????? unsigned bit_depth = 8;
??????? unsigned pixel_byte = 4;
??????? unsigned row_byte =m_size.w *pixel_byte;
??????? //設置輸出文件
??????? png_init_io(png_ptr,fp);
??????? //設置圖像屬性
??????? png_set_IHDR(png_ptr, info_ptr, m_size.w,m_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(m_size.h*sizeof(png_bytep));
??????? //填充數據
??????? for (unsignedx = 0;x < m_size.h; ++x)
??????? {//分配一行
??????????? row_pointers[x] = (png_bytep)malloc(row_byte);
??????????? for (unsignedy = 0;y < row_byte; y += pixel_byte)
??????????? {
??????????????? row_pointers[x][y + 2] = ((unsignedchar*)m_buffer)[x *row_byte + y + 0];
??????????????? row_pointers[x][y + 1] = ((unsignedchar*)m_buffer)[x *row_byte + y + 1];
??????????????? row_pointers[x][y + 0] = ((unsignedchar*)m_buffer)[x *row_byte + y + 2];
??????????????? row_pointers[x][y + 3] = ((unsignedchar*)m_buffer)[x *row_byte + y + 3];
???????????????
??????????? }
??????? }
??????? //寫入全部行
??????? png_write_image(png_ptr, row_pointers);
??????? //寫尾部
??????? png_write_end(png_ptr, info_ptr);
??????? //釋放png內存
??????? png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
?
??? ??? for (unsignedx = 0;x < m_size.h; ++x)
??????? {//釋放每行
??????????? free(row_pointers[x]);
??????? }
??????? free(row_pointers);????
??????? fclose(fp);
??? }
?
作者:略游
日期:17-06-20
QQ:1339484752
總結
以上是生活随笔為你收集整理的4.使用pnglib读写png图片的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 3.vector实现字符串类
- 下一篇: C++简单实现GC和内存池