[Home] → コンピュータ → PNG利用術

PNG利用術

This page is written in Japanese (Shift_JIS).
2008.6.16移転
2000.6.21更新(リンクの更新)
「ご意見フォーム」

目次

このページについて

 このページは,画像形式の一つであるPNGをWebページやプログラムに利用すべく格闘するページです.徐々に更新して行きますので気長に待ってやってください.新しく「WebページでPNGの特徴を活かす!」を加えました.「PNGプログラミング」と比べてより一般的に分かり易く書いたつもりです.Webページでの活用方法などご意見もお待ちしております.

 プログラミング環境に依存する項目は,Appendixという形で説明しています.著者はWindows98 + Cygwin or MS Visual C++という環境を使っていますので,一部内容が偏っているかと思いますが......上手に読んでください.

 このページの内容に関しては後述する参考サイトを元に間違いの無いよう努力しております.もし間違いがありましたら是非メールにてお知らせください.また,内容に関する責任は負いかねますのでご了承ください.著者自身は一介の日曜プログラマーであり,読者の参考たる内容になっているかは分かりませんが,お役に立てば幸いです.

PNGとは?

 PNG(Portable Network Graphics format)は,GIFとTIFFの長所を併せ持ち,さらに発展させたビットマップ画像形式と言えます.つまり,GIFのように圧縮率が高くネットワーク上で有効な形式(プログレッシブ表示を備え完全にstreamable)とTIFFのような高品質画像に適した形式の両方をサポートしています.また,GIFやTIFFとは異なりパテント・フリーであり,TIFFのようにプログラマー泣かせなほど複雑ではありません.さらに,PNGはエラーに強く(robust),異なるプラットフォームでのカラー・マッチングのためにガンマや色度を保存することができます.

 PNGはpingと発音します.非公式にはPNGは”PNG's Not GIF”の頭文字を取ったようです.PNGはGIFを引き継ぐものとして開発されました.GIFに使われているLZW圧縮法は1985年に米国Unisysが特許を取ったもので,1995年に突然LZWを利用するプログラムに対して特許権使用料を要求するようになったという背景があります(LZWのライセンス参照).TIFFもLZWをサポートしており,LZW圧縮を使ったTIFF形式もこのライセンスの対象になります.これを期に,GIFに替わるライセンス・フリーでより発展的な画像形式であるPNGが開発されました.GIFの代替形式と言うだけあって,前述したGIFに似た長所の他にPNGは透過GIF(transparent GIF)のような形式をより強化してサポートしています.ただし,一方でアニメーションGIF(animated GIF)のような形式はサポートされていません.これに関しては,別途,MNG(Multiple-image Network Graphics,mingと発音する)というPNGを基本としたアニメーションの形式が開発されています(MNG Home Page参照).ちなみに,JPEGとPNGを組み合わせたJNG(JPEG Network Graphics,jingと発音する)もドラフトの段階で,これがMNGに組み込まれることでJPEGのアニメーション形式が実現されます.

 正式なスペックを知りたい方はPNG Home Siteで手に入るPNG Spesicificationを参照してください.ちょっと古いですがRFC 2083 (rfc2083.txt)も参考になります.

PNGの特徴(箇条書き)

WebページでPNGの特徴を活かす!(まだ活かせていないけど)

 上記のようにPNGには様々な特徴がありますが,これを十分活用できるような環境はまだ揃っているとは言えません.WWWブラウザや多くの画像ソフトもようやく最近,読み込み・書き込み・表示できるようになりつつあるところで,PNGならではの特徴を表現できないものが多いようです.ここでは,PNGならではの特徴ごとに,その特徴を活かすためのヒントなどを少しずつ紹介して行きたいと思います.まだまだ先は長い......

ガンマ

 まず最初に取り上げるのはガンマです.PNG形式ではファイル・ガンマという値を保持できます.これができると何が嬉しいんでしょう?簡単に言うと「明るさを正しく表現できる」と言ったところでしょうか.ところがこのファイル・ガンマをサポートしているソフトはあまり在りませんので,この恩恵に浴することができるかは使用するソフトに依ります.「ガンマ・サポートのチェック」を参照してください.

ガンマとは?

 私達がある景色をデジカメに撮り,画像ファイルにしてCRTに表示したとします.このとき,景色からデジカメに入った光の強弱の情報が,数値化されて画像ファイルになり,CRTに表示されるときにまた光の強弱に変換されます.画面上でリアルな画像が見えるということは,「デジカメに入った光の強弱」と「CRTから目に入る光の強弱」が同じ(光の強さが比例している)ということです.このとき,ガンマは1となります.

 さて,ここで画像ファイルの数値に注目します.実は,多くの場合,「画像ファイルの数値」は「CRTから目に入る光」と比例していません.この関係は例えば以下のような単純な式で表されます.

(CRTから目に入る光の強度)=A×(画像の数値)2.2

 Aは定数です.2.2乗の「2.2」という値をガンマと言います.さて,同じように元々の景色からの光に対して画像の数値は例えば以下のような式で表されます.

(画像の数値)=B×(景色からデジカメに入った光の強度)1/2.2

 Bはやはり定数です.この式の場合,ガンマは1/2.2ですね.多くのデジカメではこのような光の強さ(画像の明るさ)の変換を行っています.デジカメの中には取り込んだ光の強さに比例した値を画像ファイルに保存できるものもあります.

 上記の2つの式を合わせると以下のようになります.

(CRTから目に入る光の強度)=A×(画像の数値)2.2
=A×(B×(景色からデジカメに入った光の強度)1/2.22.2
=C×(景色からデジカメに入った光の強度)(1/2.2)×2.2
=C×(景色からデジカメに入った光の強度)1
=C×(景色からデジカメに入った光の強度)

 Cは定数です.この式から「CRTから目に入る光」と「景色からデジカメに入った光」は比例している(全体のガンマが1)ことになり,リアルな画像が表示されていることになります.

ガンマの種類

 「ガンマとは?」の説明は非常に単純化したものですが,それでも3種類のガンマ(2.2,1/2.2,1)が出てきました.この例から全体のガンマ(1)というのは,途中のプロセスのガンマの掛算((1/2.2)×2.2)になることも分かりました.ここでは,途中のプロセスを更に細かく分類しますが,やはり個々のプロセスのガンマの掛算が全体のガンマになります.以下,画像の取り込みから表示という順で説明して行きます.

PNGツール

 ここでは,PNGに関連したツールを紹介します.取り敢えず,MS-DOSプロンプトから使用するようなソフトを紹介しておきます.

pngcheck

 pngcheckはその名の通り,PNGファイルをチェックするプログラムです.以前はソースコードしか在りませんでしたが,Windows 9x/NT用バイナリが手に入ります.PNG Source Code and Librariesからpngcheck-win32.zipをダウンロードしてやります(1998.11.4現在はver.1.99-grr0).これを適当なディレクトリに解凍して,pngcheck-static.exepngcheck.exeに変更します.

 使い方は,コマンドラインで,

> pngcheck foo.png

のように実行してやれば「ファイルにエラーが無いか・画素数・タイプ・圧縮の程度など」が表示されます.pngcheck.exeはPATHの通ったディレクトリに置いておくと便利でしょう.

tiff2png

 tiff2pngはTIFF形式のファイルをPNG形式に変換するプログラムです.変換できないファイルもままありますが,TIFFのLZW圧縮を使った形式も変換可能で,一般に良く使うタイプのTIFFファイルなら変換できると思います.しかしながら,tiff2pngはソースコードで提供されているのでビルドしてやる必要があります.Cygwinでのビルド方法を「tiff2pngのビルド」に示しますので参照してください.

 使い方は,コマンドラインで,

> tiff2png foo.tiff foo.png

のように入力してやればTIFFファイルをPNG形式に変換できます.やはりこのユーティリティもPATHの通ったディレクトリに入れておくと便利でしょう.

PNGプログラミング

 PNGファイルをプログラムで読み書きするために,PNGのスペックとにらめっこしてゼロから作るのは大変です.そこで,PNGライブラリを利用したプログラミングを行います.様々なPNGライブラリがソースやバイナリ(スタティック・ライブラリやDLL)で提供されています(PNG Source Code and Libraries参照)が,ここでは「libpng」というライブラリを使用します.

 Windowsプラットフォームの場合,libpngはまだバイナリで提供されていませんので,ソースからビルドする必要があります.Cygwinでビルドする場合は「libpngのビルド」を,MS Visual C++でビルドする場合は「libpngのビルド」を参照してください.

 さてPNGプログラミングを始めるわけですが,様々なファイル形式や細かい仕様まではなかなか行き届きません.そこで,8bit(256階調)のグレイスケール画像を取り敢えず扱うことにします.今更グレイスケールと思われるかもしれませんが,工業的に多く使われていますしカラーの基礎とも言えるということで許してください.さらに詳しい説明は「libpngのビルド」や「libpngのビルド」で紹介したexample.c, pngtest.c, libpng.txt, png.h, zlib.hを参考にしてください.また,PNGのスペックに関してはPNG Home Siteで最新のものを入手できます.

書き込み

 最も単純と思われるPNGファイルを作成するプログラムは次の通りです.エラーを恐れない荒削りなプログラムですが,分かり易いのではないかと思います.関数の説明に関してはlibpng.txt, example.cを参照してください.

#include <stdio.h>
#include <stdlib.h>
#include "png.h"

#define WIDTH  (256)
#define HEIGHT (128)

void write_png(char *file_name, unsigned char **image);
int main()
{
	unsigned char   **image;                                // image[HEIGHT][WIDTH]の形式です
	int             i, j;

	image = (png_bytepp)malloc(HEIGHT * sizeof(png_bytep)); // 以下3行は2次元配列を確保します
	for (j = 0; j < HEIGHT; j++)
	        image[j] = (png_bytep)malloc(WIDTH * sizeof(png_byte));
	for (i = 0; i < WIDTH; i++) {                           // 以下5行は単純なテストパターンを作ります
	        for (j = 0; j < HEIGHT; j++) {
	                image[j][i] = (unsigned char)i;
	        }
	}
	write_png("test.png", image);                           // PNGファイルを作成します
	for (j = 0; j < HEIGHT; j++) free(image[j]);            // 以下2行は2次元配列を解放します
	free(image);
	return 0;
}

void write_png(char *file_name, unsigned char **image)
{
	FILE            *fp;
	png_structp     png_ptr;
	png_infop       info_ptr;
	
	fp = fopen(file_name, "wb");                            // まずファイルを開きます
	png_ptr = png_create_write_struct(                      // png_ptr構造体を確保・初期化します
	                PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	info_ptr = png_create_info_struct(png_ptr);             // info_ptr構造体を確保・初期化します
	png_init_io(png_ptr, fp);                               // libpngにfpを知らせます
	png_set_IHDR(png_ptr, info_ptr, WIDTH, HEIGHT,          // IHDRチャンク情報を設定します
	                8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
	                PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
	png_write_info(png_ptr, info_ptr);                      // PNGファイルのヘッダを書き込みます
	png_write_image(png_ptr, image);                        // 画像データを書き込みます
	png_write_end(png_ptr, info_ptr);                       // 残りの情報を書き込みます
	png_destroy_write_struct(&png_ptr, &info_ptr);          // 2つの構造体のメモリを解放します
	fclose(fp);                                             // ファイルを閉じます
	return;
}

 libpngにはpng_structpng_infoの2つの主な構造体があります.png_struct構造体はlibpng関数に渡す第一引数として使いますが,ユーザがその中身を操作することはほとんどありません.png_info構造体は,PNGファイルに関する情報を与える構造体で,png_info用のインターフェイス関数を介してpng_info構造体を操作することになります.png.hファイルは忘れずにインクルードしてください.

 さて,このままでは今一つ使えませんので,write_png()関数を書き換えて,もう少しきちんとした処理をさせてみます.変更点を箇条書きにまとめてみました.

void write_png(char *file_name, png_bytepp image, int width, int height)
{
	FILE		*fp;
	png_structp	png_ptr;
	png_infop	info_ptr;
	void write_row_callback(png_structp png_ptr, png_uint_32 row, int pass);

	if ((fp = fopen(file_name, "wb")) == NULL) return;
	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (png_ptr == NULL) {
		fclose(fp);
		return;
	}
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
		fclose(fp);
		return;
	}
	if (setjmp(png_ptr->jmpbuf)) {
		png_destroy_write_struct(&png_ptr,  &info_ptr);
		fclose(fp);
		return;
	}
	png_init_io(png_ptr, fp);
	png_set_write_status_fn(png_ptr, write_row_callback);
	png_set_filter(png_ptr, 0, PNG_ALL_FILTERS);
	png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
	png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY,
		PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
	png_set_gAMA(png_ptr, info_ptr, 1.0);

	{
		time_t		gmt;		// G.M.T.
		png_time	mod_time;
		png_text	text_ptr[5];

		time(&gmt);
		png_convert_from_time_t(&mod_time, gmt);
		png_set_tIME(png_ptr, info_ptr, &mod_time);
		
		text_ptr[0].key = "Title";
		text_ptr[0].text = "Test";
		text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
		text_ptr[1].key = "Author";
		text_ptr[1].text = "Yuzo KATO";
		text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE;
		text_ptr[2].key = "Description";
		text_ptr[2].text = "Test Pattern";
		text_ptr[2].compression = PNG_TEXT_COMPRESSION_NONE;
		text_ptr[3].key = "Creation Time";
		text_ptr[3].text = png_convert_to_rfc1123(png_ptr, &mod_time);
		text_ptr[3].compression = PNG_TEXT_COMPRESSION_NONE;
		text_ptr[4].key = "Software";
		text_ptr[4].text = "test.exe";
		text_ptr[4].compression = PNG_TEXT_COMPRESSION_NONE;
		png_set_text(png_ptr, info_ptr, text_ptr, 5);
	}
	
	png_write_info(png_ptr, info_ptr);
	png_write_image(png_ptr, image);
	png_write_end(png_ptr, info_ptr);
	png_destroy_write_struct(&png_ptr, &info_ptr);
	fclose(fp);
	return;
}

void write_row_callback(png_structp png_ptr, png_uint_32 row, int pass)
{
	printf("\r%3d%% saved", (row * 100) / png_ptr->height);
}

 ここで,コールバック関数というのは画像のそれぞれの行が書き込まれる度に呼ばれる関数で,進行状況の表示などに使います.上記の例では何%まで書き込まれたかを表示します.

読み込み

 最も単純と思われるPNGファイルを読み込むプログラムを作ってみました.エラーを恐れない荒削りなプログラムですが,分かり易いのではないかと思います.関数の説明に関してはlibpng.txt, example.cを参照してください.

#include <stdio.h>
#include <stdlib.h>
#include "png.h"

int main()
{
	FILE            *fp;
	png_structp     png_ptr;
	png_infop       info_ptr;
	unsigned long   width, height;
	int             bit_depth, color_type, interlace_type;
	unsigned char   **image;
	int             i;
	
	fp = fopen("test.png", "rb");                           // まずファイルを開きます
	png_ptr = png_create_read_struct(                       // png_ptr構造体を確保・初期化します
	                PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	info_ptr = png_create_info_struct(png_ptr);             // info_ptr構造体を確保・初期化します
	png_init_io(png_ptr, fp);                               // libpngにfpを知らせます
	png_read_info(png_ptr, info_ptr);                       // PNGファイルのヘッダを読み込みます
	png_get_IHDR(png_ptr, info_ptr, &width, &height,        // IHDRチャンク情報を取得します
	                &bit_depth, &color_type, &interlace_type,
	                NULL, NULL);
	image = (png_bytepp)malloc(height * sizeof(png_bytep)); // 以下3行は2次元配列を確保します
	for (i = 0; i < height; i++)
	        image[i] = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));
	png_read_image(png_ptr, image);                         // 画像データを読み込みます
	for (i = 0; i < height; i++) free(image[i]);            // 以下2行は2次元配列を解放します
	free(image);
	png_destroy_read_struct(                                // 2つの構造体のメモリを解放します
	        &png_ptr, &info_ptr, (png_infopp)NULL);
	fclose(fp);                                             // ファイルを閉じます
	return 0;
}

 ここで,png_read_info()関数はpng_info構造体に様々な画像情報を読み込みます.コメント文の「PNGファイルのヘッダ」というのは,signatureの次のチャンク(IHDRチャンク)から画像データ(IDATチャンク)の手前のチャンクまでのことを意味します.したがって,画像データ以外のほとんどの情報をpng_read_info()関数で取得することになります.

 さて,このままでは今一つ使えませんので,一部を関数化してもう少しきちんとした処理をさせると以下のようになります.ここでは「書き込み」で書き込んだgAMAチャンクおよびtEXtチャンクの情報を表示させます.

#include <stdio.h>
#include <stdlib.h>
#include "png.h"

#define PNG_BYTES_TO_CHECK (4)

void check_if_png(char *file_name, FILE **fp);
void read_png_info(FILE *fp, png_structp *png_ptr, png_infop *info_ptr);
void read_png_image(FILE *fp, png_structp png_ptr, png_infop info_ptr,
	png_bytepp *image, png_uint_32 *width, png_uint_32 *height);

int main()
{
	FILE            *fp;
	png_structp     png_ptr;
	png_infop       info_ptr;
	unsigned char   **image;
	unsigned long   width, height;
	png_uint_32     i;
	
	check_if_png("test.png", &fp);
	read_png_info(fp, &png_ptr, &info_ptr);
	read_png_image(fp, png_ptr, info_ptr, &image, &width, &height);
{                                                               // 以下6行はgAMAチャンクを取得し表示します
	double          file_gamma;
	
	if (png_get_gAMA(png_ptr, info_ptr, &file_gamma))
	        printf("gamma = %lf\n", file_gamma);
}
{                                                               // 以下8行はtEXtチャンクを取得し表示します
	png_textp       text_ptr;
	int             num_text;
	
	if (png_get_text(png_ptr, info_ptr, &text_ptr, &num_text))
	        for (i = 0; i < num_text; i++)
	                printf("%s = %s\n", text_ptr[i].key, text_ptr[i].text);
}
	for (i = 0; i < height; i++) free(image[i]);
	free(image);
	png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
	fclose(fp);
	return 0;
}

void check_if_png(char *file_name, FILE **fp)
{
	char    sig[PNG_BYTES_TO_CHECK];
	
	if ((*fp = fopen(file_name, "rb")) == NULL)
	        exit(EXIT_FAILURE);
	if (fread(sig, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) {
	        fclose(*fp);
	        exit(EXIT_FAILURE);
	}
	if (!png_check_sig(sig, PNG_BYTES_TO_CHECK)) {
	        fclose(*fp);
	        exit(EXIT_FAILURE);
	}
}

void read_png_info(FILE *fp, png_structp *png_ptr, png_infop *info_ptr)
{
	*png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (*png_ptr == NULL) {
	        fclose(fp);
	        exit(EXIT_FAILURE);
	}
	*info_ptr = png_create_info_struct(*png_ptr);
	if (*info_ptr == NULL) {
	        png_destroy_read_struct(png_ptr, (png_infopp)NULL, (png_infopp)NULL);
	        fclose(fp);
	        exit(EXIT_FAILURE);
	}
	if (setjmp((*png_ptr)->jmpbuf)) {
	        png_destroy_read_struct(png_ptr, info_ptr, (png_infopp)NULL);
	        fclose(fp);
	        exit(EXIT_FAILURE);
	}
	png_init_io(*png_ptr, fp);
	png_set_sig_bytes(*png_ptr, PNG_BYTES_TO_CHECK);
	png_read_info(*png_ptr, *info_ptr);
}

void read_png_image(FILE *fp, png_structp png_ptr, png_infop info_ptr,
	png_bytepp *image, png_uint_32 *width, png_uint_32 *height)
{
	png_uint_32     i, j;
	
	*width = png_get_image_width(png_ptr, info_ptr);
	*height = png_get_image_height(png_ptr, info_ptr);
	if ((*image = (png_bytepp)malloc(*height * sizeof(png_bytep))) == NULL) {
	        fclose(fp);
	        exit(EXIT_FAILURE);
	}
	for (i = 0; i < *height; i++) {
	        (*image)[i] = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));
	        if ((*image)[i] == NULL) {
	                for (j = 0; j < i; j++) free((*image)[j]);
	                free(*image);
	                fclose(fp);
	                exit(EXIT_FAILURE);
	        }
	}
	png_read_image(png_ptr, *image);
}

 ここで新しく作った関数は,check_if_png(), read_png_info(), read_png_image()の3つです.check_if_png()関数では,PNGファイルを開いた後で,signatureの8バイトの内PNG_BYTES_TO_CHECKバイト分だけ読み込んでPNGファイルかどうかチェックします.上記のプログラムではPNG_BYTES_TO_CHECKは4バイトにしてあります.read_png_info()関数は,PNGの構造体を確保・初期化して,それらに値を読み込みます.read_png_image()では,画像データ用の配列を確保して,そこに画像データを読み込みます.

 さて,ファイル・ガンマ値が入っているので,リアルな表示をさせるにはread_png_image()関数を以下のように書き換えてやります.ここでは,Windowsで表示させることを想定します(つまり,ディスプレイ・ガンマが2.2と仮定します).また,sRGBチャンクは使用していないものとします.

void read_png_image(FILE *fp, png_structp png_ptr, png_infop info_ptr,
	png_bytepp *image, png_uint_32 *width, png_uint_32 *height)
{
	png_uint_32     i, j;
	double          display_gamma, file_gamma;
	
	display_gamma = 2.2;
	if (png_get_gAMA(png_ptr, info_ptr, &file_gamma))
	        png_set_gamma(png_ptr, display_gamma, file_gamma);
	else
	        png_set_gamma(png_ptr, display_gamma, 0.50);
	png_read_update_info(png_ptr, info_ptr);
	*width = png_get_image_width(png_ptr, info_ptr);
	*height = png_get_image_height(png_ptr, info_ptr);
	if ((*image = (png_bytepp)malloc(*height * sizeof(png_bytep))) == NULL) {
	        fclose(fp);
	        exit(EXIT_FAILURE);
	}
	for (i = 0; i < *height; i++) {
	        (*image)[i] = (png_bytep)malloc(png_get_rowbytes(png_ptr, info_ptr));
	        if ((*image)[i] == NULL) {
	                for (j = 0; j < i; j++) free((*image)[j]);
	                free(*image);
	                fclose(fp);
	                exit(EXIT_FAILURE);
	        }
	}
	png_read_image(png_ptr, *image);
}

 上記のようにpng_set_gamma()関数で適切なガンマを設定し,png_read_update_info()関数でpng_info構造体の内容を更新した後で,画像データを読み込む形となります.この場合,配列に読み込まれた画像データは適当なガンマ変換をされた値になります.

用語解説

参考サイト

Appendix A: Cygwin(GNU-Win32)環境

Cygwinとは?

 CygwinツールはGNUツールをWindows上に移植したもので,UNIXライクな環境をWindows上で使うことができ,gccでWindowsアプリケーションもビルドできます.これまで作られた多くのUNIXのプログラムも,ソースをほとんどいじることなくビルドできる点がCygwinプロジェクトの特徴です.これを実現するためWin32 APIではサポートされていないUNIXに不可欠な機能をCygwin API(cygwin.dll)というライブラリで補っています.

インストール

 1998年11月1日(日本時間)に公開されたB20からGNU-Win32(あるいはCygwin32)という名称もCygwinに変更されました.インストール方法は簡単で,The Cygwin Projectからfull.exeをダウンロードして実行するだけです(前のバージョンが入っているディレクトリへのPATHははずしてください).

 インストールが終了したら,そのままの設定では不便なので,以下のような手順でカスタマイズすることをお勧めします.ただし,インストール先のディレクトリをE:\Cygnus,作業ディレクトリをF:\Workとして話を進めます.

  1. C:\autoexec.batPATH=%PATH%;.;E:\CYGNUS\CYGWIN~1\H-I586~1\BINという一行を加えコンピュータを再起動する(実行ファイルがCygwinのDLLを参照できるようにパスを通してやる)
  2. C:\Windows\スタート メニュー\プログラム\Cygnus Solutions\Cygwin B20のプロパティを下記のように変更する
    プログラム・タブのコマンドラインC:\WINDOWS\COMMAND.COM
    プログラム・タブの作業ディレクトリF:\Work
    プログラム・タブのバッチ ファイルE:\CYGNUS\CYGWIN~1\CYGNUS.BAT
    メモリ・タブの環境変数の初期サイズ2048
  3. E:\CYGNUS\CYGWIN-B20\CYGNUS.BAT
    SET PATH=E:\CYGNUS\CYGWIN~1\H-I586~1\BIN;%PATH%
    の行を削除する
  4. [スタート]→[プログラム]→[Cygnus Solutions]でCygwin B20を選択し,Cygwinのコマンドラインで以下のコマンドを入力する
    $ umount /
    $ mount -f e:\\Cygnus\\cygwin-b20\\H-i586-cygwin32 /
    $ mount -f e:\\Cygnus\\cygwin-b20\\H-i586-cygwin32\\i586-cygwin32 /usr
    $ mkdir /usr/local
    $ mount -f c:\\Windows\\temp /tmp

 ステップ4のコマンドラインでは,BASH.EXE-2.02$プロンプトが表示されていることから分かるように,bashが起動しています.したがって,上記のコマンドのように\は\\のように2回打つ必要があります.ところで,ライブラリをインストールするときに,ライブラリ・ファイルやそのヘッダ・ファイルは/usr/local/libや/usr/local/includeへコピーされます.上記の設定では,E:\cygnus\cygwin-b20\h-i586-cygwin32\i586-cygwin32\localの下のlibやincludeディレクトリにコピーされることになります.

zlibのビルド

 libpngはPNGファイルの圧縮・解凍のためにzlibを用いますので,ここでzlibをビルドしてみます.1998年8月16日現在では最新と思われるzlib 1.1.3のファイルzlib.tar.gzzlib Home Pageから作業ディレクトリにダウンロードします.次に,Cygwinのコマンドラインで作業ディレクトリに移り以下のコマンドを入力します.

$ tar xzvf zlib.tar.gz
$ cd zlib-1.1.3
$ ./configure
$ make test

 これで,libz.aが作られたはずです.zlibを利用するには,libz.a, zlib.h, zconf.hの3つのファイルが必要になりますので,適当なディレクトリに入れておいてください.libz.a/usr/local/libに,zlib.h, zconf.h/usr/local/includeにコピーしたい場合は以下のコマンドを実行します.

$ make install

libpngのビルド

 libpngのソースファイルを作業ディレクトリにダウンロードします.1998年8月16日現在ではver.1.0.2が新しいようなので,libpng-1_0_2_tar.gzPNG Source Code and Librariesからダウンロードします.zlibと同様,Cygwinのコマンドラインで作業ディレクトリに移り以下のコマンドを入力します.

$ tar xzvf libpng-1_0_2_tar.gz
$ cd libpng-1.0.2
$ cp scripts/makefile.std makefile
$ chmod +w makefile

 上記の最後ののコマンドは,これから編集したいmakefileがリードオンリー・ファイルになっている(makefile.stdがリードオンリー・ファイルになっているから)ので必要です.makefileを以下のように書き換えてください(この場合,libz.a/usr/local/libに,zlib.hzconf.h/usr/local/includeに在るものとします).

#ZLIBLIB=/usr/local/lib → ZLIBLIB=/usr/local/lib
#ZLIBINC=/usr/local/include → ZLIBINC=/usr/local/include
ZLIBLIB=../zlib → #ZLIBLIB=../zlib
ZLIBINC=../zlib → #ZLIBINC=../zlib
CC=cc → CC=gcc

 makefile編集後,libpng-1.0.2ディレクトリで以下のコマンドを入力します.

$ make test

 新しくlibpng.aが作られ,これを使った簡単なプログラムpngtest.exeが実行されます.pngtest.exepngtest.pngという PNG形式のファイルを読み取り,これとまったく同じ内容のpngout.pngというファイルを出力するプログラムです.正しくpngtest.pngと同じ内容のpngout.pngというファイルができているか確認してみてください.

 libpngを利用するには,libpng.a, png.h, pngconf.hの3つのファイルが必要になりますので,適当なディレクトリに入れておいてください.libpng.a/usr/local/libに,png.h, pngconf.h/usr/local/includeにコピーしたい場合は以下のコマンドを実行します.

$ make install

 libpngを使ってプログラムを作る場合,libpng-1.0.2ディレクトリにあるexample.cや先ほどのpngtest.cを参考にすると分かり易いようです.libpng.aを使う上での詳しい説明はlibpng.txtを,libpngの使用方法や制限に関してはpng.hを,zlibを使う上での説明やzlibの制限に関してはzlib.hを参考にしてください.

libtiffのビルド

 libtiffはftp://ftp.sgi.com/graphics/tiff/からダウンロードできます.1998.10.20現在で最も新しそうなtiff-v3.4beta037.tar.gzを作業ディレクトリにダウンロードして以下のコマンドで解凍・展開してやります.

$ tar xzvf tiff-v3.4beta037.tar.gz

 本来は(UNIX系のOSならば),ここで作業ディレクトリの下のtiff-v3.4beta037ディレクトリに移り./configureと入力してmakefileを作るところですが,Window 9x環境では上手く動作しません(Windows NTでは上手く動作するかもしれません).そこで,Cygwin B20用にmakefileを作ってみました.このmakefileはhttp://www5.cds.ne.jp/~kato/png/src/libtiffmake.zipからダウンロードできますので,tiff-v3.4beta037\libtiffディレクトリに解凍してください.

 Cygwinのコマンドラインでtiff-v3.4beta037\libtiffディレクトリに移り以下のコマンドを入力すればライブラリがビルドされます.

$ make

 このとき,tif_luv.cmath.hでダブってlog2()関数が定義されているのでWarningが出ますが,同じ内容の関数なので問題は無いでしょう.これで,libtiff.aが作られたはずです.libtiffを利用するには,libtiff.a, tiff.h,tiffio.h,tiffcomp.hの4つのファイルが必要になりますので,適当なディレクトリに入れておいてください.libtiff.a/usr/local/libに,tiff.h,tiffio.h,tiffcomp.h/usr/local/includeにコピーしたい場合は以下のコマンドを実行します.

$ make install

tiff2pngのビルド

 tiff2png.exeはlibpng,zlibに加え後述するlibtiffというライブラリを使用しますので,これらをインストールしておく必要があります.それぞれ「zlibのビルド」,「libpngのビルド」,「libtiffのビルド」を参照してください.

 tiff2pngはPNG Source Code and Librariesからダウンロードできます(1998.10.20現在,最新のファイルはtiff2png-0_6_tar.gz).このファイルを作業ディレクトリにダウンロードして,Cygwinのコマンドラインで作業ディレクトリに移り以下のコマンドを入力します.

$ mkdir tiff2png
$ tar xzvf tiff2png-0_6_tar.gz -C tiff2png

 tiff2pngディレクトリの下にはalphaとlibtiffというディレクトリができます.このlibtiffディレクトリは「libtiffのビルド」でビルドしたlibtiffの古いヴァージョンのものなので無視します.ここで,tiff2pngディレクトリのMakefileを以下のように書き換えます(インクルード・ファイルが/usr/local/includeにライブラリ・ファイルが/usr/local/libに在る場合).

CC=cc  → CC=gcc
CFLAGS=-DOLD_LIBTIFF -L. \
	-I$(LIBTIFF) \
	-I$(LIBPNG) \
	-I$(ZLIB)
 → CFLAGS=-DNEW_LIBTIFF -I/usr/local/include
LDFLAGS=-L. \
	-L$(LIBTIFF)/ \
	-L$(LIBPNG)/ \
	-L$(ZLIB)/ \
	-lpng -lz -ltiff -lm
 → LDFLAGS=-L/usr/local/lib -lpng -lz -ltiff -lm -luser32

 次に,Cygwinのコマンドラインで,tiff2pngディレクトリに移りmakeと入力してやればtiff2png.exeがビルドされるはずです.u_char, u_short, u_int, u_longがダブって定義されているというWarningが出ますがこれも問題無いでしょう.

 それではtiff2png.exeのテストを行います.Cygwinのコマンドラインで,tiff2png\alphaディレクトリに移ります.そのディレクトリのalpha.shというシェル・スクリプトを実行してやるのですが,このファイルの先頭行に#!/bin/bashを書き加えないと実行できません.alpha.shを実行してやると,このディレクトリの中のTIFFファイルがPNGに変換されるようすが表示されます.同じくこのディレクトリの中のalpha.htmlをHTMLブラウザで開き,画像が正しく表示されていればOKです.

Appendix B: MS Visual C++環境

 MS Visual C++(以下MSVC)の統合環境(Developer Studio)には馴染めないので,勝手ながらコマンドラインを使用することにします.MSVCをコマンドラインで使用するには環境変数を設定しなければなりません.その場合,インストール先ディレクトリのDevStudio\Vc\bin\Vcvars32.batに設定内容が書かれていますが,簡単に書くと以下のようになります(ただし,MSVCのインストール先ドライブはE:とします).

@echo off
set MSDevDir=E:\DevStudio\SharedIDE
set MSVCDir=E:\DevStudio\VC
set PATH="E:\DevStudio\SharedIDE\BIN;E:\DevStudio\VC\BIN";"E:\DevStudio\VC\BIN\WIN95";%PATH%
set INCLUDE=E:\DevStudio\VC\INCLUDE;E:\DevStudio\VC\MFC\INCLUDE;E:\DevStudio\VC\ATL\INCLUDE;%INCLUDE%
set LIB=E:\DevStudio\VC\LIB;E:\DevStudio\VC\MFC\LIB;%LIB%

 この内容をC:\autoexec.batに追加しても良いのですが,GNU-Win32の場合と同様に以下の手順でMSVCのコマンドライン環境を作ることをお勧めします.

zlibのビルド

 libpngはPNGファイルの圧縮・解凍のためにzlibを用いますので,ここでzlibをビルドしてみます.1998年8月16日現在では最新と思われるzlib 1.1.3のファイルzlib.tar.gzzlib Home Pageから作業ディレクトリにダウンロードし解凍します.次に,MSVCのコマンドラインで作業ディレクトリの下のzlib-1.1.3ディレクトリに移り以下のコマンドを入力します.

> nmake /f msdos\makefile.w32

 これで,zlib.libが作られたはずです.zlibを利用するには,zlib.lib, zlib.h, zconf.hの3つのファイルが必要になりますので,適当なディレクトリに入れておいてください.zlib.libE:\DevStudio\VC\libに,zlib.h, zconf.hE:\DevStudio\VC\includeにコピーしておくと便利です(ただし,MSVCのインストール先ドライブはE:とします).

zlib.dll

 「zlibのビルド」ではzlibのスタティック・ライブラリをビルドしましたが,この替わりにzlibのDLLを使うことも出来ます.zlib DLLはZLIB DLL Home Pageからダウンロードできます.1999年1月15日現在手に入れることが出来るzlib113dll.zipを解凍すると,dll32ディレクトリにzlib.dllzlib.libが見つかるはずです.このzlib.libzlib.dllのインポート・ライブラリで「zlibのビルド」でビルドしたzlib.libと名前は同じですが中身は違いますので注意してください.

 zlib.dllを使うには,インポート・ライブラリの方のzlib.libおよびzlib.h, zconf.hが必要になりますので,適当なディレクトリに入れておいてください.これらのヘッダ・ファイルはzlib113dll.zipには含まれていないので,「zlibのビルド」と同様にzlib 1.1.3のファイルzlib.tar.gzをダウンロード&解凍して,zlib-1.1.3ディレクトリからコピーしてください.

 例えば,zlib-1.1.3ディレクトリにあるexample.cをビルドするには,MSVCのコマンドラインで以下のようなコマンドを入力します.

> cl /D "_WINDOWS" /D "ZLIB_DLL" example.c /link zlib.lib

 下記の「libpngのビルド」でも,makefile.w32を編集してCFLAGSマクロに/D "_WINDOWS" /D "ZLIB_DLL"を加えることでzlib.dllを使ったlibpngをビルドすることが出来ます.

libpngのビルド

 libpngのソースファイルを作業ディレクトリにダウンロードします.1998年8月16日現在ではver.1.0.2が新しいようなので,libpng-1_0_2_tar.gzPNG Source Code and Librariesからダウンロードし解凍します.作業ディレクトリの下のlibpng\scripts\makefile.w32を使用しますが,なぜかリードオンリー・ファイルになっているので書き込み可能に変更しておきます.makefile.w32をエディタでみると,

all: libpng.lib

という行が見つかると思うので,以下のように書き換えてやります(上記のままだとpngtest.exeがビルドされない).

all: libpng.lib pngtest.exe

 また,以下の箇所の..\zlib\zlib.libzlib.libと書き直す必要があります.

pngtest.exe: pngtest.obj libpng.lib
	$(LD) $(LDFLAGS) pngtest.obj libpng.lib ..\zlib\zlib.lib /OUT:pngtest.exe /SUBSYSTEM:CONSOLE

 MSVCのコマンドラインで作業ディレクトリの下のlibpng-1.0.2に移り,以下のコマンドを入力します.

> nmake /f scripts\makefile.w32

 これで,libpng.libやpngtest.exeができたはずです.Cygwinと同様にpngtest.exeを実行して,pngtest.pngと同じ内容のpngout.pngというファイルができたか確認してみてください.

 libpngを利用するには,libpng.lib, png.h, pngconf.hの3つのファイルが必要になりますので,適当なディレクトリに入れておいてください.libpng.libE:\DevStudio\VC\libに,png.h, pngconf.hE:\DevStudio\VC\includeにコピーしておくと便利です(ただし,MSVCのインストール先ドライブはE:とします).

 libpngを使ってプログラムを作る場合,libpng-1.0.2ディレクトリにあるexample.cや先ほどのpngtest.cを参考にすると分かり易いようです.libpng.libを使う上での詳しい説明はlibpng.txtを,libpngの使用方法や制限に関してはpng.hを,zlibを使う上での説明やzlibの制限に関してはzlib.hを参考にしてください.


緑の月Webサイト
このPNG画像はIEforWin4.0以降やNN4.04以降などのWWWブラウザで見られます
Copyright © 1998-2008 Yuzo KATO
Please feel free to link to this page!