2010年09月23日

PNGファイルの書き込み



DIBデバイス独立ビットマップ)をPNGファイルに書き込む場合は、以下のような流れになります。

  1. DIBハンドルからDIBSECTION構造体を取得し、必要となる各値を算出する。
  2. DIBカラーテーブルがあれば取得し、PNGパレットに変換する。
  3. DIBイメージデータPNGイメージフォーマットに変換する。
  4. PNGファイルに書き込む。



libpngを使用するためのインクルード定義です。

#include "png.h"


libpngのインストール」でダウンロードした二つのフォルダーから、以下のファイルを今回のプログラムを格納したファイルのあるフォルダーにコピーしておきます。

lpng143」フォルダーからコピーするファイル

png.h
pngconf.h

zlib」フォルダーからコピーするファイル

zlib.h
zconf.h

PNGファイルを新たに作成して、_PNGStreamOut関数を呼び出します。

//DIBをPNGファイルに書き出す
BOOL __stdcall _SavePNG(LPCTSTR lpszFileName,HBITMAP hbmDIB)
{
CFile outfile;
if (!outfile.Open(lpszFileName,CFile::modeCreate|CFile::modeWrite)) return FALSE;
BOOL bResult=_PNGStreamOut(&outfile,hbmDIB);
outfile.Close();
return bResult;
}


ファイル書き込みのためのユーザー定義関数です。

//PNGファイルフラッシュ関数
void _PngFlushFunc(png_structp png_ptr)
{
CFile* outfile=(CFile*)png_get_io_ptr(png_ptr); //CFileクラスのポインタを取得する
}

//PNGファイル書き込み関数
void _PngWriteFunc(png_structp png_ptr, png_bytep buf, png_size_t size)
{
CFile* outfile=(CFile*)png_get_io_ptr(png_ptr); //CFileクラスのポインタを取得する
outfile->Write(buf,(UINT)size); //CFileクラスを使ってPNGファイルに書き込む
}


DIB画像からPNGストリームを作成し、ファイルに書き出します。

//PNGファイルストリームの書き込み
BOOL __stdcall _PNGStreamOut(CFile* outfile,HBITMAP hbmDIB)
{
ASSERT(outfile->IsKindOf(RUNTIME_CLASS(CFile)));

//DIBのヘッダー情報の読み込み-------------------------------------------------

//DIBのDIBSECTION構造体の取得
DIBSECTION ds={0};
//DIBSECTION構造体の取得に失敗したら、FALSEを返して終了する。
if (!::GetObject(hbmDIB,sizeof(DIBSECTION),&ds)) return FALSE;

//指定DIBがDIBでない時は、FALSEを返して終了する。
if (ds.dsBmih.biSize!=sizeof(BITMAPINFOHEADER)) return FALSE;

//PNG構造体の取得
png_structp png_ptr=NULL;
png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
if (!png_ptr) return FALSE;

//PNG情報構造体の取得
png_infop info_ptr=NULL;
info_ptr=png_create_info_struct(png_ptr);
if (!info_ptr){
//PNG情報構造体の取得に失敗したら、PNGのヘッダー情報を削除し
//FALSEを返して終了する。
png_destroy_write_struct(&png_ptr,NULL);
return FALSE;
}

//DIBの幅
UINT width =ds.dsBmih.biWidth;
//DIBの高さ(絶対値)
UINT height=abs(ds.dsBmih.biHeight);
//DIBのビット深度
UINT nBpp=ds.dsBmih.biBitCount;
//ビットマップバイト幅
DWORD widthBytes=ds.dsBm.bmWidthBytes;
//ピクセルデータのバイト数
DWORD dwSizeImage=widthBytes*height;

//カラーテーブルのサイズを算出
UINT nColors=0;
//カラーテーブルがある場合で、使用色数が0でないなら、エントリー数には使用色数を、
//それ以外は2のnBpp乗を代入します。

if (nBpp<=8) nColors=(ds.dsBmih.biClrUsed)? ds.dsBmih.biClrUsed:1<<nBpp;

//カラーテーブルの取得
png_colorp png_palette=0; //PNGに読ませるPNGパレット

if (nColors){
//DIBからカラーテーブルを取り出す関数
RGBQUAD* pColorTable=new RGBQUAD[nColors];
::ZeroMemory(pColorTable,sizeof(RGBQUAD)*nColors);
HDC hMemDC=::CreateCompatibleDC(0);
HGDIOBJ hOldObj=::SelectObject(hMemDC,hbmDIB);
nColors=::GetDIBColorTable(hMemDC,0,nColors,pColorTable);
::SelectObject(hMemDC,hOldObj);
::DeleteDC(hMemDC);
if (nColors){
//DIBカラーテーブルの取り出しに成功したら、
//PNGパレットにコピーする。
png_palette=new png_color[nColors];
for(UINT i=0;i<nColors;i++) {
png_palette[i].blue =pColorTable[i].rgbBlue;
png_palette[i].green=pColorTable[i].rgbGreen;
png_palette[i].red =pColorTable[i].rgbRed;
}
}
delete[]pColorTable;
//DIBカラーテーブルの取り出しに失敗したら、FALSEを返して終了する。
if (!nColors) return FALSE;
}

//これ以降の内部エラーはここに戻り、FALSEを返して終了する。
png_bytepp image_rows=0; //PNGに読ませるピクセルデータを行単位に格納するバッファ

if (setjmp(png_ptr->jmpbuf)) {
if (png_palette) delete[]png_palette;
if (image_rows) {
for(UINT i=0;i<height;i++) delete[]image_rows[i];
delete[]image_rows;
}
//PNGのヘッダー情報の削除
png_destroy_write_struct(&png_ptr,&info_ptr);
return FALSE;
}

//DIBのイメージデータをPNGイメージに変換する----------------------------------

image_rows=new PBYTE[height];
BYTE b,g,r,a; //ピクセルの色を格納する変数
WORD w; //16ビットピクセルを一時格納する変数
int shg,shr,shb; //16ビットピクセルから色を取り出す時の各RGBのシフト値
int color_type=PNG_COLOR_TYPE_RGB;//PNG画像の形式

switch(nBpp){
case 1:
case 4:
case 8://パレットDIBの時
color_type=PNG_COLOR_TYPE_PALETTE;
for(UINT i=0;i<height;i++){
UINT row=(ds.dsBmih.biHeight<0)?i:(height-1-i);
PBYTE dst=image_rows[i]=new BYTE[widthBytes];
::CopyMemory(dst,(PBYTE)ds.dsBm.bmBits+row*widthBytes,widthBytes);
}
break;

case 16://16ビットRGBDIBの時はピクセルデータを24ビットに変換する
//RGB565の時
if (ds.dsBitfields[0]==0xF800){
shr=8;
shg=3;
shb=3;
}
else{//RGB555の時
shr=7;
shg=2;
shb=3;
}

//ピクセルデータを24ビットに変換する
for(UINT i=0;i<height;i++){
UINT row=(ds.dsBmih.biHeight<0)?i:(height-1-i);
PBYTE dst=image_rows[i]=new BYTE[width*3];
WORD* src=(WORD*)((PBYTE)ds.dsBm.bmBits+row*widthBytes);
for(UINT j=0;j<width;j++){
w=*src++;
r=(BYTE)((w&ds.dsBitfields[0])>>shr);
g=(BYTE)((w&ds.dsBitfields[1])>>shg);
b=(BYTE)((w&ds.dsBitfields[2])<<shb);
*dst++=r;
*dst++=g;
*dst++=b;
}
}
break;

case 24://24ビットRGBDIBの時
for(UINT i=0;i<height;i++){
UINT row=(ds.dsBmih.biHeight<0)?i:(height-1-i);
PBYTE dst=image_rows[i]=new BYTE[width*3];
PBYTE src=(PBYTE)ds.dsBm.bmBits+row*widthBytes;
for(UINT j=0;j<width;j++){
b=*src++; //青
g=*src++; //緑
r=*src++; //赤
*dst++=r;
*dst++=g;
*dst++=b;
}
}
break;

case 32://32ビットRGBADIB
color_type=PNG_COLOR_TYPE_RGB_ALPHA;

for(UINT i=0;i<height;i++){
UINT row=(ds.dsBmih.biHeight<0)?i:(height-1-i);
PBYTE dst=image_rows[i]=new BYTE[width*4];
PBYTE src=(PBYTE)ds.dsBm.bmBits+row*widthBytes;
for(UINT j=0;j<width;j++){
b=*src++; //青
g=*src++; //緑
r=*src++; //赤
a=*src++; //アルファ値
*dst++=r;
*dst++=g;
*dst++=b;
*dst++=a;
}
}
break;
}

//PNGファイルに書き込む------------------------------------------------------

//書き込み関数の設定
png_set_write_fn(png_ptr,(png_voidp)outfile,(png_rw_ptr)_PngWriteFunc,(png_flush_ptr)_PngFlushFunc);

//PNGヘッダーの設定
if(nBpp>8){//RGB画像の時
png_set_IHDR(png_ptr,info_ptr,width,height,8,color_type,PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);
}
else {//パレットがある時
png_set_IHDR(png_ptr,info_ptr,width,height,nBpp,color_type,PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);
//1,2,4のビット深度のピクセルを、1バイトに8,4,2ピクセルずつパックします。
png_set_packing(png_ptr);
//PNGパレットを書き込む
png_set_PLTE(png_ptr,info_ptr,png_palette,nColors);
}

//PNG情報構造体の書き込み
png_write_info(png_ptr,info_ptr);
//イメージデータの書き込み
png_write_image(png_ptr,image_rows);
//書き込み終了
png_write_end(png_ptr,info_ptr);
//PNGのヘッダー情報の削除
png_destroy_write_struct(&png_ptr,&info_ptr);

//後始末
if (png_palette) delete[]png_palette;
for(UINT i=0;i<height;i++) delete[]image_rows[i];
delete[]image_rows;
return TRUE;
}


戻り値がTRUEなら、PNGファイルへの書き込みは成功です。

サンプルプログラム(VisualC++net2003ソリューション)PNGFileTest02.zip

上記のzipファイルをダウンロードして、前々回の「libpngのインストール」で作成した「lpng143」フォルダーと「zlib」フォルダーがある同じフォルダー内にWinRAR等を使って解凍すると、「PNGFileTest02」と言う名のフォルダーが作成されます。



「PNGFileTest02」フォルダー内にある「PNGFileTest.sln」を開いて「F5」キーを押すと、ビルド確認のダイヤログが表示されるので「Yes」を選択してソリューションをビルドします。

ビルドが終わると、直ちにプログラムが自動起動してウィンドウが表示されます。ウィンドウ内にあるトラッカーの範囲内で、右クリックしてポップアップメニューで「開く」を選択して、「ファイルを開く」ダイヤログを表示させ、resフォルダー内の「1729_01.png」を読み込みます。



画像が表示されたところで「ファイル」/「名前を付けて保存」で「ファイル名を付けて保存」ダイヤログを表示させ、「1729_01-Copy.png」と名づけて「保存」ボタンを押します。



ツールバーの「新規」で画像を一旦を消しておいてから、先ほど作成した「1729_01-Copy.png」を読み込んで、元の画像と同じ画像が表示されるか確認します。






posted by ひろし at 01:34| Comment(0) | PNGファイルフォーマット | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。