007-10(1)  前へ←  ホームへ     -   

No.007 - 10_2  PNGファイル(2)  PNG操作の手順


PNGの読込・保存の手順です。
 png.h 訳  libpng.txt 訳  readme 訳  LISENCE 訳  TODO 訳

PNG配布ファイルにある説明があまりに冗長で的を得ないので、なんとかまとめて書き出しました。
ほとんど libpng.txt からの流用なんですが、正直 libpng.txt を真っ向から読むのはだるいので、
とりあえず下記の文を読んで予備知識を積んでからlibpng.txtを読むといいんじゃないかな〜と思います。


○. 実際のおおまかな手順


T.読込み
U.書込み
V.各チャンクの情報・設定方法

構造体

PNG操作では png_struct と  png_info 構造体がメインとなりますが、
どちらもメンバ変数に直接アクセスするのではなく、主にそのポインタを使って
png_get_*() と png_set_*() 系の関数で操作します。
(メンバ変数に直接アクセスするとバージョン間の互換性が保証されません)

例外:png_struct のpng_ptr->jmpbuf への直接アクセスはOKだそうです。

横幅と高さの限界

横幅と高さはPNG 仕様書には 2^31-1 (0x7fffffff) 、又は21.47億の行と列まで大丈夫と書いてあります(メモリが足りれば)。
実際は、画像が大きすぎると、png_error() が呼ばれエラーになります。
libpng のテストでは100万行くらいまでやったそうです(どんな画像だよ)。

T.読み込み


初期化と設定







1. シグネチャ読込み
  ↓
2.メモリ初期化とエラー処理設定
  ↓
3.ファイル・ポインタのセット
  ↓
4.コールバック関連の設定
  ↓ 
データ読込み













6.データ 読込み
A 高水準読込インタフェース
  画像を一括して読込みます

B 低水準読込インタフェース
  ↓
6B-1.情報読込み
  ファイル・ヘッダの情報チャンクを読み込みます
  ↓
6B-2.入力変換設定 (インタレース設定含む)
  画像データのピクセル変換が主になります。
  ↓
6B-3.画像データ読込
  実際の画像データを読み込みます
    @一括読込み   A各行読込み(インタレース 処理含む)
終了処理


7.画像データの後 にあるチャンクの読み 込み
  ↓
8.メモリ解放


1.シグネチャ読込み



まずファイルの最初の8バイトを png_sig_cmp() 関数でチェックします。
PNG シグネチャのバイトと一致すれば 0 を返し、そうでなければ非ゼロを返します。

シグネチャ
FILE *fp = fopen(file_name, "rb");
if (!fp) return (ERROR);

fread(header, 1, number, fp);

is_png = !png_sig_cmp(header, 0, number);
if (!is_png) return (NOT_PNG);


2.メモリ初期化とエラー処理設定


次に、png_struct と png_info のメモリ確保・初期化です。
構造体サイズがDLL のlibpng と同じになるようにします。
エラー処理関数及びその構造体へのポインタもセットしますが、デフォルトのハンドラを使うならNULLで大丈夫です。
構造体メモリ確保関数は、生成に失敗すると黙ってNULLを返すので、アプリケーション側でそれを確認します。

メモリ確保・初期化

// png_struct 構造体の初期化
png_structp png_ptr = png_create_read_struct (
                   PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, user_error_fn, user_warning_fn);
if (!png_ptr) return (ERROR);

// png_info 構造体の初期化(画像データの前にあるチャンク用)
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
    png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
    return (ERROR);
}

// png_info 構造体の初期化(画像データの後にあるチャンク用)
png_infop end_info = png_create_info_struct(png_ptr);
if (!end_info) {
    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
    return (ERROR);
}

libpng 内部でエラーに出くわしたら longjmp() でユーザが呼び出したそのルーチンに戻ることになるので、
setjmp(png_jmpbuf(png_ptr))を呼んでおく必要があります。
また異なるルーチンで、png_*() 関数呼ぶような場合、その都度 jmpbuf 変数を更新しておいて下さい。

setjmp/longjmp の情報に関しては自分のコンパイラの setjmp/longjmp の文書を見て下さい。
エラーが発生し、libpng の longjmp で自分の setjmp に制御がきたら、メモリを解放するため png_destroy_read_struct() を呼んで下さい。

setjmp/longjmp
if (setjmp(png_jmpbuf(png_ptr))) {
    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
    fclose(fp);
    return (ERROR);
}

もし、setjmp/longjmp が面倒なら、PNG_SETJMP_NOT_SUPPORTED で libpng をコンパイルすれば、
エラー時、デフォルトでabort() になっている PNG_ABORT() が呼ばれます。


3.ファイル・ポインタのセット


読込み関数はデフォルトでは C 関数 fread() を使います。なので有効な FILE* を png_init_io() に渡して下さい。
必ずファイルはバイナリ・モードで開いておいてください。
ファイル・ポインタのセット
png_init_io(png_ptr, fp);

// 事前にシグネチャを読込確認済なら、ファイル先頭から読み飛ばしているバイト数を知らせる
png_set_sig_bytes(png_ptr, number); 


4.コールバックの設定


ここでコールバック関数をセットできますが、何も設定しなくてもかまいません。
ただコールバックをセットしておけば、たとえ高水準読込インタフェースでも制御がアプリケーション側に返って来るので
うまく使えば処理が簡単になるかもしれません。
セットするコールバック関数は自分で用意しなければなりません。

チャンク用コールバックです。
PNG仕様書のチャ ンクのレイウアトなどを参考に処理して下さい。
チャンク用自前コールバック
// 自作コールバック関数  …"read_chunk_callback" ではなくて、自分の好きな関数名も使えます
read_chunk_callback(png_ptr ptr, png_unknown_chunkp chunk){
   /* 自前チャンクデータを含む不定チャンク構造体: */
       png_byte name[5];
       png_byte *data;
       png_size_t size;
   /* CRC処理はすでに libpng が管理してます。 */

   /* 自前コードはここに書いてください。下記のうち一つを返すようにしてください: */

       return (-n); /* チャンク・エラー有り */
       return (0); /* 判別不能 */
       return (n); /* 成功 */
}

// 自作コールバック関数を libpng にセット
png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, read_chunk_callback);

各行を読込んだ後に呼ばれるコールバック関数も設定できます。プログレス・バー系のものを作る用です。
各行用自前コールバック
// 自前コールバック関数  …関数名は read_row_callbackでなくても大丈夫です
void read_row_callback(png_ptr ptr, png_uint_32 row, int pass){
     /* 自前コード */
     // row 引数は行番号
     // pass 引数はインタレースのパスの値 …下記参照
}

// 自前コールバック関数をlibpng にセット
png_set_read_status_fn(png_ptr, read_row_callback);


不定チャンクの処理方法についても設定できますが、
デフォルト通りlibpng に破棄させてしまうなら何も設定しなくてもかまいません。
libpng.txt の png_set_keep_unknown_chunks() を見て下さい


5.データ読込み


5A. 高水準読込インタフェース

高水準読込インタフェースは、変換フラグも使い、メモリに画像を一括 して読込みます
    png_read_png(png_ptr, info_ptr, png_transforms, NULL)

png_transforms 引数は変換フラグ数値を論理OR演算で組み合わせます。
    PNG_TRANSFORM_IDENTITY     無変換
PNG_TRANSFORM_STRIP_ALPHA アルファ・チャンネル破棄
PNG_TRANSFORM_PACKING 1, 2, 4 ビットサンプルをバイト単位に拡張
PNG_TRANSFORM_INVERT_MONO モノクロ画像反転
PNG_TRANSFORM_BGR RGB → BGR、RGBA → BGRA の反転
PNG_TRANSFORM_INVERT_ALPHA 不透過 → 透過へアルファ変更
(ここにあるのは一部設定だけです) 

この呼び出しは png_read_info() 〜png_read_image() 〜 png_read_end() と同じです。
png_read_png() を使うなら png_set_transform()は呼び出してはいけません。

png_read_png() の呼出し後、画像データを取得するには
   png_bytep row_pointers[height];   // 各行のピクセル・データのポインタ配列
   row_pointers[i] = png_get_rows(png_ptr, info_ptr);

前もって画像サイズ及びピクセルサイズがわかるなら、次のようにpng_read_png() を呼び出す前にrow_pointersにメモリ確保できます。

事前に画像メモリを確保
if (height > PNG_UINT_32_MAX/png_sizeof(png_byte)) png_error (png_ptr, "Image is too tall to process in memory");
if (width > PNG_UINT_32_MAX/pixel_size) png_error (png_ptr, "Image is too wide to process in memory");

row_pointers = png_malloc(png_ptr, height*png_sizeof(png_bytep));

for (int i=0; i<height, i++) row_pointers[i]=png_malloc(png_ptr, width*pixel_size);
png_set_rows(png_ptr, info_ptr, &row_pointers);

png_set_rows() を使うのであれば、
アプリケーションがrow_pointers メモリを解放します(または row_pointers[i] が分割メモリであった場合)。

もし事前に row_pointers メモリが確保してないなら、
png_read_png() が確保してくれますし、解放も png_destroy_*() がやってくれます。




 5B. 低水準読込インタフェース
   
6B-1.情報読込み
    
低水準では、実際のデータに対応した全ファイル情報を読込む準備を行います。画像データ自体は含みません。

読込み済みの info_ptr から画像情報を取得する関数です。
    png_read_info(png_ptr, info_ptr);

png_read_end() で読込が終わるまで、データ全部は入ってないかもしれないので気をつけて下さい。
  チャンク情報は下記 png_get_〜()で取得できます。

6B-2.入力変換設定(インタレース設定)

カラータイプやビット深度の変更、バイト・オーダーの変更などの設定がココでできます。
ひとつひとつはかなり細かい関数なので、ここでは紹介しませんが
libpng.txt に書いてある入力変換の 項を見れば、直感的にすぐわかると思います。

各種データ変換はそれらが設定された順番で反映されていきます。

インタレースをlibpng 側に処理させるなら、ココでインタレースを設定しなければなりません。
(ブラウザみたくインタレース途中表示したいなら上記コールバックの設定して処理します)
    number_of_passes = png_set_interlace_handling(png_ptr);

変換を設定した後、
libpng に入力変換設定を適用させるため png_info 構造体を更新します。
    png_read_update_info(png_ptr, info_ptr);

6B-3.画像データ読込
    メモリ確保後、画像データを読み込みます。
 
@シンプルな一発読込み

一番シンプルな方法は関数を一つ呼び出すだけです。
十分なメモリを確保して、png_read_image() を呼ぶと、libpng が画像データを全て読み込んでくれます。
その際、各行へのポインタの配列を渡す必要があります。
この関数はインタレースも自動的に処理します。
   png_bytep row_pointers[height];
   png_read_image(png_ptr, row_pointers);

row_pointers はピクセルに使っている型のポインタか void か char ポインタです。

A高技術を要する各行読み込み

一気に画像全部は読み込むのでなく
少しずつ読み込んでいくのなら、 png_read_rows() が使えます。
インタレースが無ければ(IHDR チャンクで interlace_type == PNG_INTERLACE_NONE)、簡単です。
    png_read_rows(png_ptr, row_pointers, NULL, number_of_rows);
row_pointers …行の配列
number_of_rows …行の配列の数

一回に一つずつしか処理しないのなら、row_pointers の配列ではなく単体の row_pointerで処理できます:
    png_bytep row_pointer = row;
    png_read_row(png_ptr, row_pointer, NULL);

インタレース(IHDR チャンクで interlace_type != 0)なら、ちょっと面倒です。


現行のPNG用インタレース・タイプはAdam7(interlace_type == PNG_INTERLACE_ADAM7)しかありません。
このインタレースは、8x8 グリッドをベースとした7つの画像に分解します。

libpng で画像を拡張するとき、png_start_read_image() や png_read_update_info() を呼ぶ前に
上記のタイミングで png_set_interlace_handling()関数を呼んで下さい:
    if (interlace_type == PNG_INTERLACE_ADAM7) number_of_passes = png_set_interlace_handling(png_ptr);

png_set_interlace_handling()は必要なパス数を返します。
現在のところは7ですが、将来変わるかもしれません。非インタレースなら、返り値は1パスです。

png_read_rows()に第3引数に行バッファを渡し、第2引数をNULLにして
コールバックで number_of_passes を確認してください。
各パスはそのパスに割り当てられたピクセルだけ書き込みます。
    png_read_rows(png_ptr, NULL, row_pointers, number_of_rows);

インタレースの細かい処理を libpng に任せないのなら、png_read_rows() を7回呼んでください。
8x8 グリッド各画像を自分で連結して下さい。
イ ンタレースで渡されるパスのピクセル位置順序
1 6 4 6 2 6 4 6
7 7 7 7 7 7 7 7
5 6 5 6 5 6 5 6
7 7 7 7 7 7 7 7
3 6 4 6 3 6 4 6
7 7 7 7 7 7 7 7
5 6 5 6 5 6 5 6
7 7 7 7 7 7 7 7
第1パスは横 1/8、縦 1/8 (列0/8、行0/8)
第2パスは横 1/8、縦 1/8 (列4/8、行0/8)
第3パスは横 1/4、縦 1/8 (列0/4、行4/8)
第4パスは横 1/4、縦 1/4 (列2/4、行0/4)
第5パスは横 1/2、縦 1/4 (列0/2、行2/4)
第6パスは横 1/2、縦 1/2 (列1/2、行0/2)
第7パスは横は奇数スキャンラインのを全部


7.画像データの後に付いてるチャンク

画像データの後に付いてるコメントや時間チャンクを読み込むには
png_read_end()関数に png_info 構造体を分けて引数として渡します。
いらないなら引数はNULLで構いません。
   png_read_end(png_ptr, end_info);

8.メモリ解放


以下のようにlibpng で確保したメモリを解放します:
   png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);

png_destroy_read_struct() 関数はlibpng 内部で確保されたメモリを一括で解放しますが

png_free_data() 関数で、libpng で確保されたチャンク・メモリを個別に解放することもできますし、
動作も png_data_freer() 関数で変更することができます。
png_free_data() 関数と png_data_freer() 関数についてはlibpng.txtを見て下さい。


U.書込み



初期化と設定









1.ファイル開く
  ↓ 
2.メモリ確保とエラー処理関数のセット
  ↓ 
3.ファイル・ポインタのセット
  ↓ 
4.コールバック関数の設定
  ↓ 
5.情報チャンクの設定
  ↓ 
データ書込み









6.画像データ書込み
A.高水 準書込みインタフェース
  一括書込み

B.低水準書込みインタフェース
  ↓
6B-1. 情報書込み
  ↓
6B-2. 書込み変換設定
  ↓
6B-3. 画像データの書込み
    @一括書込み   A各行書込み(インタレース 処理含む)
終了処理


7.画像データの後にあるチャンクの書込み
  ↓
8.メモリ解放



ほとんど読込と一緒です。

1.ファイル開く


ファイルをバイナリ書込みで開きます。
ファイル開く
FILE *fp = fopen(file_name, "wb");
if (!fp) {
    return (ERROR);
}

2.メモリ確保とエラー処理関数のセット

次に、png_struct と png_info のメモリ確保と初期化を行います。返り値がNULLでないかチェックして下さい。

読込も書込みも両方に"png_ptr"という名前は使うと分からなくなるので
ホントは"read_ptr" や "write_ptr"といったようにポインタ名を変えたほうがいいかもしれません。。

メモリ確保
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, user_error_fn, user_warning_fn);
if (!png_ptr) return (ERROR);

png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
   png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
   return (ERROR);
}

構造体の用意ができたら、エラー処理の設定です。
libpng 内部でエラーに出くわしたら longjmp() でユーザが呼び出したルーチンに戻ることになるので、
setjmp(png_jmpbuf(png_ptr))を呼んでおく必要があります。

また異なるルーチンで、png_*() 関数呼ぶような場合、その都度 jmpbuf 変数を更新しておいて下さい。
setjmp/longjmp の情報に関しては自分のコンパイラの setjmp/longjmp の文書を見て下さい。

setjmp/longjmp
if (setjmp(png_jmpbuf(png_ptr))){
   png_destroy_write_struct(&png_ptr, &info_ptr);
   fclose(fp);
   return (ERROR);
}
...
return;

setjmp/longjmp が面倒なら、libpng をPNG_SETJMP_NOT_SUPPORTED でコンパイルすることもできます。
その場合のエラーは abort() がデフォルトに設定されているPNG_ABORT()を呼ぶことになります。

3.ファイル・ポインタのセット

さて今度は、出力コードの設定です。デフォルトのlibpng ではC関数のfwrite() を使います。
これを使うなら、有効な FILE * を関数 png_init_io() に渡して下さい。

ファイル・ポインタのセット
png_init_io(png_ptr, fp);


4.書込みコールバック関数の設定

プログレス・バーの表示などができるように、各行の書込み後に呼ばれるコール バック関数の設定を行います。
書込みコールバック
// 書込みコールバック関数  …"write_row_callback"でなくて、何か違う好きな名前でも構いません
void write_row_callback(png_ptr, png_uint_32 row, int pass) {
  /* 自分のコードをココに書く */
}

// libpng に自分の関数をセットします。
png_set_write_status_fn(png_ptr, write_row_callback);

ライブラリの圧縮方法に対し少し変更オプションを付けられますが一般的ではありません。
使ったとしてもpng_set_compression_level() くらいです。   
    png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);  // zlib 圧縮レベルをセット
この関数を呼ばなければ適度な速度/圧縮率で実行されます。

他にもpng_set_compression_*() 関数はzlib 圧縮ライブラリへのインタフェースですが、
その多くは動作未確認のようです。


5.情報チャンクの設定

ヘッダ・チャンク書込みを行います。
画像の前に書込むpng_info 構造体のデータはすべて入力しておく必要があります。
png_info のチャンクのセットについては下記参照願います。

IHDR チャンク<画像ヘッダ>は必ずセットして下さい。
パレット画像なら PLTE チャンクも必ずセットして下さい。
その他必要に応じてセットして下さい。

画像の後でも書込みが可能なのはテキストチャンクや時間チャンクだけですので注意して下さい。
画像データの後まで待つのものはpng_write_end()までその構造体を完成させなくて大丈夫です。

6.画像データ書込み


6A.高水準書込みインタフェース

情報構造体をセットして、次の関数を使います。
    png_write_png(png_ptr, info_ptr, png_transforms, NULL)

png_transforms 引数には変換フラグを論理ORで入れた整数が入ります。
  
PNG_TRANSFORM_IDENTITY   無変換
PNG_TRANSFORM_PACKING 1, 2 4 ビット・パック・サンプル
PNG_TRANSFORM_INVERT_MONO モノクロ画像反転
PNG_TRANSFORM_BGR RGB → BGR、 RGBA → BGRA の変換
PNG_TRANSFORM_INVERT_ALPHA アルファを不透明から透明へ変更
(ここにあるのは一部設定だけです)

この呼び出しは、png_write_info() → png_write_image() → png_write_end() と同等です。
(最後の引数は、予約済みゼロです)

png_write_png() を使うなら、必ずpng_transforms 引数を使い、どの png_set_transform() 関数も使ってはいけません。


6B.低水準書込みインタフェース

 
6B-1. 情報書込み

低水準ルートで行くのなら、まずファイル情報をpng_write_info() で書込みます。
    png_write_info(png_ptr, info_ptr);

PNGでは通常アルファ値は不透明度ですが、透明度として扱いたいなら、png_write_info() の前に反転します。
0 が完全透明で255(8ビット)や65535(16ビット)が完全不透明にします。
    png_set_invert_alpha(png_ptr);

この変換は、パレット画像なら必ず png_write_info() の前に呼んで下さい。tRNS チャンクを反転のためです。
パレット画像でないなら、png_write_info() を呼んだ後にこの変換を安全に処理することができます。

6B-2. 書込み変換設定

ファイル情報を書き込んだ後、ピクセルに特殊な変換処理を行う設定をすることができます。
各種のデータ変換方法はそれらが生じた順序で反映されていきます。
カラータイプやビット深度を変更するような変換などもあります。
これについてはlibpng.txt出力変換設定を見て下さい。

(実際、こういうピクセル変換は書込み前に自分でやったほうが融通もきくし早いと思います)
 
 6B-3. 画像データの書込み

@ <一括書込み>

  
これの一番シンプルな方法ではひとつ png_write_image() を呼ぶだけで済みます。
各行のポインタの配列を渡して下さい。
関数が自動的にインタレース処理も行います。
    png_byte *row_pointers[height];   // void*か char*、またはユーザ指定のピクセルポインタを入れておく。
    png_write_image(png_ptr, row_pointers);


A <ちょっとずつ書込み>


いっぺんに画像全部は書き込まない場合は、png_write_rows() を使います。
ファイルをインタレースしないなら、もう単純に:
    png_write_rows(png_ptr, row_pointers, number_of_rows);

1回1行ずつ書込んで行くなら、row_pointer の配列ではなく row_pointer 1個だけでも処理できます:
    png_bytep row_pointer = row;
    png_write_row(png_ptr, row_pointer);

ファイルをインタレース化するなら、処理はちょっと複雑になるのでうまいことやって下さい。
現在、PNGインタレースは、画像を7つの小さな画像に分解する"Adam7"だけです。
libpng にこれらの組み立てをさせるか、自分でそれを行うことになります。

インタレースの処理をlibpng に任せるには、7つの分割画像分、
  png_set_interlace_handling() を使って png_write_rows() を呼んでください。

    number_of_passes = png_set_interlace_handling(png_ptr);   // 必要なパス数が返ります。現在は 7 です。
    png_write_rows(png_ptr, row_pointers, number_of_rows);   // number_of_passes 回、書込む


7.画像データの後のチャンク書込み

画像データの後にコメントや時間チャンクを書込むことができます。
適切に情報を満たしたpng_info ポインタを渡して下さい。興味がなければNULLで構いません。
    png_write_end(png_ptr, info_ptr);

8.メモリ解放

これが終わったら、libpng で使ったメモリを全て解放します。
    png_destroy_write_struct(&png_ptr, &info_ptr);

png_destroy_read_struct() 関数はデフォルトの動作ではlibpng 内部でメモリ確保されたメモリを一括で解放するだけです。
png_free_data() 関数で、libpng で確保されたチャンク・メモリを個別に解放することもできます。
png_data_freer() 関数で動作を変更することができます。

png_free_data() 関数と png_data_freer() 関数についてはlibpng.txtを見て下さい。


V.各チャンクのデータの読込みと書込み
  読込み時はpng_get_〜()
  書込み時はpng_set_〜()

IHDRチャンク  <画像ヘッダ・チャ ンク>
画像のメイン情報を扱います
// 読込み用関数
png_get_IHDR(png_ptr, info_ptr, &width, &height,
                &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method);
// 書込み用関数
png_set_IHDR(png_ptr, info_ptr, width, height,
                 bit_depth, color_type, interlace_type, compression_type, filter_method)
width - 画像ピクセル横幅が入ります (2^31 まで)。
height - 画像ピクセル高さが入ります (2^31 まで)。
bit_depth - 画像のチャンネル1個分のビット深度。 有効値は 1, 2, 4, 8, 16 。
color_type - カラー/アルファ チャンネル の設定。
PNG_COLOR_TYPE_GRAY (ビット深度 1, 2, 4, 8, 16)
PNG_COLOR_TYPE_GRAY_ALPHA (ビット深度 8, 16)
PNG_COLOR_TYPE_PALETTE (ビット深度 1, 2, 4, 8)
PNG_COLOR_TYPE_RGB (ビット深度 8, 16)
PNG_COLOR_TYPE_RGB_ALPHA (ビット深度 8, 16)

PNG_COLOR_MASK_PALETTE
PNG_COLOR_MASK_COLOR
PNG_COLOR_MASK_ALPHA
filter_method - (PNG 1.0 では必ず PNG_FILTER_TYPE_BASE 。MNG-1.0 ストリーム埋込なら PNG_INTRAPIXEL_DIFFERENCING にもなる。
compression_type - (PNG 1.0 で必ず PNG_COMPRESSION_TYPE_BASE )
interlace_type - (PNG_INTERLACE_NONE または PNG_INTERLACE_ADAM7)
  interlace_typeに興味がないなら、compression_type、 filter_methodは NULL で構いません。
channels = png_get_channels(png_ptr, info_ptr);
channels - カラータイプ情報のチャンネル値 (有効値は、1 (GRAY, PALETTE), 2 (GRAY_ALPHA), 3 (RGB), 4 (RGB_ALPHA または RGB + フィルタタイプ))
rowbytes = png_get_rowbytes(png_ptr, info_ptr);
rowbytes - 行を保持するのに必要なバイト数。
signature = png_get_signature(png_ptr, info_ptr);
signature - ファイル(か何か)から読込んだシグネチャを保持。データはシグネチャを読込んだそのまま同じオフセットを保持します (つまりアプリケーションが libpng を開始させる前にシグネチャを4バイト読込んであれば、残った4バイトは signature[4] 〜 signature[7] に入ります(png_set_sig_bytes()を見て下さい))。
  
width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
color_type = png_get_filter_type(png_ptr, info_ptr);
filter_method = png_get_filter_type(png_ptr, info_ptr);
compression_type = png_get_compression_type(png_ptr, info_ptr);
interlace_type = png_get_interlace_type(png_ptr, info_ptr);


以下は各データ・チャンクです。

読込み時
当然そのチャンクが読込まれて無ければ無効なチャンクです。
png_get_valid(png_ptr, info_ptr, PNG_INFO_<chunk>)png_get_<chunk>(png_ptr, info_ptr, ...) 関数は、
データが読込んであれば非ゼロを返し、そうでなければゼロを返します。
png_get_<chunk>へ渡す引数は、簡単なデータ型なら直接引数に指定するようにされていますが
そうでない複雑な型ならば引数の info_ptr ポインタに値が返されます。
PLTEチャンク   <パレット・チャ ンク>
png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
palette - パレット  (png_color 配列)
num_palette - パレット数

PLTE があるときPLTE チャンクの前に何かチャンクを書き込みたいなら、
2ステップでPNG 情報を書き込むようにして、その間に自分自身のチャンクを挿入します:
    png_write_info_before_PLTE(png_ptr, info_ptr);
    png_set_unknown_chunks(png_ptr, info_ptr, ...);
    png_write_info(png_ptr, info_ptr);

gAMAチャンク   <ガンマ・チャン ク>
png_get_gAMA(png_ptr, info_ptr, &gamma);
gamma - ガンマ値  (PNG_INFO_gAMA)

sRGBチャンク   <sRGBチャン ク>
png_get_sRGB(png_ptr, info_ptr, &srgb_intent);
png_set_sRGB(png_ptr, info_ptr, srgb_intent);
srgb_intent - 表現仕様(PNG_INFO_sRGB)
sRGB チャンクの存在はピクセルデータがsRGBカラー空間にあることを意味します。
チャンクは gAMA と cHRM の仕様値 にも暗に関連します。
表現意図は International Color Consortium で定義されたCSS-1 特性です。

PNG_sRGB_INTENT_SATURATION,
PNG_sRGB_INTENT_PERCEPTUAL,
PNG_sRGB_INTENT_ABSOLUTE
PNG_sRGB_INTENT_RELATIVE.
のうち値をひとつとります。
書込み用
png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, srgb_intent);
srgb_intent - 表現仕様(PNG_INFO_sRGB)
sRGB チャンクの存在はピクセルデータがsRGBカラー空間にあることを意味します。
この関数は書込み済みのsRGB と一貫した仕様値の gAMA と cHRM チャンクにもまた起因します。

iCCPチャンク   <iCCPチャン ク>
png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile, &proflen);
png_set_iCCP(png_ptr, info_ptr, name, compression_type, profile, proflen);
name - プロフィール名
compression - 圧縮タイプ; 常にPNG 1.0用 PNG_COMPRESSION_TYPE_BASE。この引数が NULL なら無視。
profile - International Color Consortium (国際色協会 ICC)カラー・プロフィールデータ。 NULs (10進値0のオクテット) を含むかも。
proflen - プロフィール・データのバイト長。

sBITチャンク   <有効ビット・ チャンク>
png_get_sBIT(png_ptr, info_ptr, &sig_bit);
png_set_sBIT(png_ptr, info_ptr, sig_bit);
sig_bit - 読込み時
与えられたカラータイプ(png_color_16)に適した、グレー、赤、緑、青のそれぞれのチャンネルの有効ビット数(PNG_INFO_sBIT)

与えられたカラータイプ(png_color_16)に適していようといまいと
グレー、赤、緑、青チャンネルの (PNG_INFO_sBIT)用の有意ビットの数です。

tRNSチャンク   <透明度チャン ク>
png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
png_set_tRNS(png_ptr, info_ptr, trans, num_trans, trans_values);
trans - 透過パレット・エントリの配列 (PNG_INFO_tRNS)
trans_values - 非パレット画像用の単一透過色のカラーサンプル値かグレーレベル (PNG_INFO_tRNS)
num_trans - 透過エントリの数 (PNG_INFO_tRNS)

hISTチャンク   <パレット・ヒス トグラム・チャンク>
png_get_hIST(png_ptr, info_ptr, &hist);
png_set_hIST(png_ptr, info_ptr, hist);


(PNG_INFO_hIST)
hist - パレットのヒストグラム (png_uint_16の配列)

tIMEチャンク   <最終更新時間 チャンク>
png_get_tIME(png_ptr, info_ptr, &mod_time);
png_set_tIME(png_ptr, info_ptr, mod_time);
mod_time - 画像の最終更新時間  (PNG_VALID_tIME)

libpng は Y2K に準拠しているので年は4桁で扱います。
tIME チャンクは 65535 までの年を保持できます。


bKGDチャンク   <背景色チャン ク>
png_get_bKGD(png_ptr, info_ptr, &background);
background - color_type に関係なく、有効な 16ビット 赤、緑、青の値の背景色

textチャンク   <テキスト・チャ ンク>
num_comments = png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
png_set_text(png_ptr, info_ptr, text_ptr, num_text);
num_comments - コメント数
text_ptr - コメントを保持している png_text の配列です。
各png_text 構造体は言語コード、キーワード、テキスト値、圧縮タイプを保持します。
text_ptr[i].compression - "text" で使う圧縮タイプ

- PNG_TEXT_COMPRESSION_NONE  // 非圧縮
PNG_TEXT_COMPRESSION_zTXt
PNG_ITXT_COMPRESSION_NONE  // 非圧縮
PNG_ITXT_COMPRESSION_zTXt

// 書き込み時
有効値は画像データの圧縮と同じ数値です。現在のところ、有効な数値はゼロだけです。
言語コードや変換されたキーワードがまだ書き出されてないのにPNG_TEXT_COMPRESSION_NONE や PNG_TEXT_COMPRESSION_zTXt を指定しても、tEXt と zTXt チャンクは言語変数を持てません。
1000 バイトくらいまでは、圧縮する価値がありません。
text_ptr[i].key - コメント用キーワード。  1〜79 文字。
PNG仕様書にあるいくつかの典型キーワードが、ほとんどそのまま使われま す。
同じキーワードを繰り返し使うこともできます。
text_ptr[i].text - カレント・キーワード用テキストコメント。NULL や空でもOK。
text_ptr[i].text_length - 解凍後のテキスト文字列長。0 は iTXt 用
text_ptr[i].itxt_length - 解凍後のitxt文字列長。0 は tEXt/zTXt用
text_ptr[i].lang - コメントの言語 (空文字列なら不定)。
text_ptr[i].lang_key - UTF-8 でのキーワード(空文字列なら不定)。
num_text - コメント数 (num_comments と同じ; ここが NULL なら重複回避になります)。

テキストチャンクは画像の前にも後にもそれぞれ書き込むことができます。

テキスト、言語、変換キーワードはNULL も含め、png_get_textが返した構造体は、png_set_text() に渡されるときふつうのC文字列になります。みな空文字列になることもありますが、決してNULLポインタにはなりません。

テキストチャンクの実データについて
キーワード/テキストは1チャンクにつき1ペアでコメントはいってます。
テキスト・チャンク数は無制限ですが、サイズは2^31バイトが限界です。
キーワードとテキストは人間に読めるようになるべく省略をしないで書いてください。

いくつ同じキーワードがあっても構いません。
text_ptr は png_text 構造体の配列で、それぞれ言語文字列へのポインタ、キーワード・ポインタ、テキスト文字列へのポインタを保持しています。
テキスト文字列、言語コード、変換キーワードは空かNULLポインタでも構いません。
キーワード/テキストのペアはもとの来た順番で配列に格納されます。
しかし、テキストチャンクは画像データの前後にあったりするので、ほかのデータとごっちゃにならないようにして下さい。

text がファイルに書き出された後、圧縮タイプはPNG_TEXT_COMPRESSION_NONE_WR や PNG_TEXT_COMPRESSION_zTXt_WR にセットされるので、最後( png_write_end() )まで2度と書き出されません。

キーワードは必ず要りますが、テキスト文字列は非圧縮ペアなら省くことができます。圧縮ペアならテキスト文字列も必要です。
テキスト文字列は圧縮したとしても、あんまり圧縮の意味がないと思います。

PNG仕様書によるキーワード:

Title 短い(1行) タイトルか 画像のキャプション
Author 画像の作者名
Description 画像に関する記述 (長くても可)
Copyright 著作権表示
Creation Time 画像の最初の作成時間 (通常 RFC 1123 フォーマットです)
マシンが日付を解析しやすいように、"Creation Time"の 
RFC 1123形式の日付(例: "22 May 1997 18:07:10 GMT")を使うことが推奨されます(絶対ではありません)。
PNG 時間→RFC 1123形式の文字列の変換を行う関数 png_convert_to_rfc1123(png_timep) があります。

Software 画像作成に使用したソフトウェア
Disclaimer 法的放棄声明
Warning 中身の特性に関する警告
Source 画像作成に使用したデバイス
Comment 雑多コメント;他の画像形式への変換




sPLTチャンク   <推奨パレット・ チャンク>
num_spalettes = png_get_sPLT(png_ptr, info_ptr, &palette_ptr);
png_set_sPLT(png_ptr, info_ptr, &palette_ptr, num_spalettes);
palette_ptr - 読込み時、1つ以上のsPLTチャンクの内容を持つパレット構造体の配列を読込みます。
書き込み時、情報構造体内のパレットのリストへ加えられるpng_sPLT_struct 構造体の配列。
num_spalettes - 読込み時、読込んだsPLT チャンク数。
書き込み時、加えられるパレット構造体の数。
 
oFFsチャンク   <オフセット・ チャンク>
png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &unit_type);
png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
offset_x - スクリーン左端からの正値オフセット
offset_y - スクリーン上端からの正値オフセット
unit_type - PNG_OFFSET_PIXEL、PNG_OFFSET_MICROMETER

pHYsチャンク   <物理ピクセル解 像度・チャンク>
png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type);
png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
res_x - ピクセル/単位 のX方向の物理解像度
res_y - ピクセル/単位 のY方向への物理解像度
unit_type - PNG_RESOLUTION_UNKNOWN、
PNG_RESOLUTION_METER

sCALチャンク
png_get_sCAL(png_ptr, info_ptr, &unit, &width, &height)
png_set_sCAL(png_ptr, info_ptr, unit, width, height)
unit - 物理スケール単位 (整数)
width - 物理スケール単位でのピクセル横幅
height - 物理スケール単位でのピクセル高さ


(width は height は double型)

sCAL_sチャンク
png_get_sCAL_s(png_ptr, info_ptr, &unit, &width, &height)
png_set_sCAL_s(png_ptr, info_ptr, unit, width, height)
unit - 物理スケール単位 (整数)
width - 物理スケール単位でのピクセル横幅
height - 物理スケール単位でのピクセル高さ

(width と height は"2.54"のような文字列)

不定チャンク
num_unknown_chunks = png_get_unknown_chunks(png_ptr, info_ptr, &unknowns)
png_set_unknown_chunks(png_ptr, info_ptr, &unknowns, num_unknowns)
unknowns - 不定チャンクが入っている png_unknown_chunk 構造体の配列
unknowns[i].name - 不定チャンク名
unknowns[i].data - 不定チャンクデータ
unknowns[i].size - 不定チャンクデータサイズ
unknowns[i].location - ファイル中のチャンク位置

// 書き込み時 …ファイルにチャンクを書込む位置
0: チャンクを書込みません
PNG_HAVE_IHDR: PLTEの前
PNG_HAVE_PLTE: IDATの前
PNG_AFTER_IDAT: IDATの後

"location"メンバは既に出力ファイルに書込み済みの部分と自動的に一致します。
png_set_unknown_chunks() を呼んだ後に変更することができます。
それぞれの"location" の中は、チャンクは構造体(入力ファイルから読み込むか、順序を示す "i"の値です)内の位置が一致するようにシーケンスされます。

読込み時
"i"という値はPNGファイルから読み込んだりpng_set_unknown_chunks() で挿入したチャンクの順番に相当します。

書き込み時
チャンクに名前を付けて、生データ、サイズ; それに入ってる全部のデータをセットします。png_write_info_before_PLTE 関数や、png_write_info関数、png_write_end function 関数で書き込まれます。





png_time 構造体を使った時間変更について
2つの変換ルーチンがあり、time_t 構造体用のpng_convert_from_time_t() と tm 構造体用のpng_convert_from_struct_tm() です。
time_t 構造体のルーチンは gmtime() 関数を使います。
どっちかを使わなくても構いませんが、png_time 構造体に直接書き込みたいなら、なるべくローカル時間でなくグリニッジ標準時 (GMT) を使ってください。
年の数字は全表示(例: 98ではなく 1998、… PNG は 2000 年問題準拠です!)で、月は1からスタートすることに留意して下さい。

画像作成時の時間を記録するなら、"Creation Time"キーワードのプレーン tEXt チャンクを使います。
(なぜかというと"creation time"自体があやふやで、作成された時点では非PNGだったりするためです)

libpng は Y2K に準拠しています。
png_convert_from_time_t() … pngwrite.cにあります。

年の数値は  png_time_struct の "png_uint_16 year"
年の文字列は  png.c 中のローカル・キャラクタ文字列png_struct の "png_charp time_buffer" と "near_time_buffer"

png_convert_from_time_t() 関数はgmtime() を呼んで返された(year - 1900) のシステム時間を変換して4桁の年数にします。






 007-09(2)  前へ←  ホームへ  →次へ  007-10(2)