読者です 読者をやめる 読者になる 読者になる

GameProgrammar's Night

ゲームプログラム系の覚え書き

libpng Simplified API 解説

 ver.1.6から、簡単にpngデータの読み書きができるように追加されたAPI群です。試しに使って見たら、ほんとに簡単だったので紹介したいと思います。

Simplified APIで出来ること

 画像サイズやピクセルフォーマット、ベタデータ(解凍済み)になったデータ部を扱うことができます。
 代わりにチャンクへの直接的なアクセスはできませんので、補助チャンクの情報が欲しい時は使えません。

基本

 SimplifiedAPIは、png_image構造体が基点になります。
 png_imageは、幅・高さ・フォーマットなど画像としての基本情報を扱います。BITMAPINFOHEADERやFT_Faceなどに相当する構造体です。
 png_image構造体を、png_imageで始まる各種関数に渡しながら使っていきます。

読み出し

手順

  1. png_image構造体初期化
  2. png_image_begin_read_from_file関数で、画像情報を取得
  3. 画像情報を元にデータ部を格納できるバッファを確保
  4. png_image_finish_read関数で、データ部を取得
  5. すべての処理が終わったら、png_image_free関数で解放

 png_image_begin_read_from_file関数には、png_image_begin_read_from_memory関数というメモリを対象にした関数もありますので、必要に応じて使い分けます。

サンプルコード

// 宣言と初期化
png_image png;
memset(&png, 0, sizeof(png);
png.version = PNG_IMAGE_VERSION; // これを忘れずに!

// 読み出したいpngの基本情報を取得する
png_image_begin_read_from_file(&png, "yukikaze.png");

// エラーメッセージなどはpng_image構造体に設定されるので、それをチェック
// 次からは省略します
if(PNG_IMAGE_FAILED(png))
{
   std::cout << png.message << endl;
   return;
}

// 読み出した情報に合わせて、データ部を格納するバッファを確保
uint32_t stride = PNG_IMAGE_ROW_STRIDE(png);
uint8_t  buf    = new uint8_t[PNG_IMAGE_BUFFER_SIZE(png,stride)];

// データ部を取得
png_image_finish_read(&png, NULL, buf, stride, NULL);

// bufを使って色々する


// 使い終わったら解放
delete[] buf;
png_image_free(&png);

 PNG_IMAGE_で始まるのはすべてマクロです。1ピクセル当たりのバイト数とか、必要なバッファサイズなどを計算してくれます。

 バッファには、png_image構造体のformatフラグに合わせたフォーマットで、左上から右下に向かってベタデータが書き込まれます。
 フォーマットフラグは、PNG_FORMAT_で始まるマクロとして定義されています。
 png形式が対応するすべてのフォーマットが扱えますが、ここでは一番使うであるRGB/RGBAを紹介します。

format 意味
PNG_FORMAT_RGB 1ピクセルごとに、RGBの巡に1バイトずつ並ぶ。値の範囲は0~255
PNG_FORMAT_RGBA 1ピクセルごとに、RGBAの巡に1バイトずつ並ぶ。値の範囲は0~255

書き出し

手順

  1. png_image構造体初期化
  2. png_image構造体に書き出すための情報(幅・高さ・フォーマットなど)を設定
  3. 設定した情報に合わせたデータを書き込んだバッファを用意
  4. png_image_write_to_file関数で、書き込む
  5. png_image_freeで後始末

サンプルコード

BMPをpngに変換します。

bitmap bmp;
bmp.load("kaga.bmp");

// png_image構造体初期化
png_image png;
memset(&png, 0, sizeof(png));
png.version = PNG_IMAGE_VERSION;

// 必要な情報を設定
png.width  = bmp.width();
png.height = bmp.height();
png.format = PNG_FORMAT_RGB;

uint32_t stride = PNG_IMAGE_ROW_STRIDE(png);

uint8_t buf[/*イメージサイズ*/]; // bmpのデータを左上から右下RGBの並びに変換したとする

// pngファイルに変換
png_image_write_to_file(&png, "kaga.png", 0, buf, stride, NULL);

 png_image_write_to_file関数の第5引数のstrideを負にして渡すと上下反転されます。 
 もちろん、png_image_write_to_file関数には、png_image_write_to_memory関数というメモリに書き出すバージョンも用意されています。