あまつぶ

9.5 【RGBColorとインデックス】

 IconPartyでのGIF保存で、clip2gifに保存を依託するルーチンを作ってみた。IconPartyでは、LZWを使わないmi-GIFという方法でGIFファイルを作成している。mi-GIFは基本的にランレングス圧縮なので、単純な図形ならそれなりに圧縮できるが、そうでない画像については圧縮効率があまりよくない。そこで、clip2gifに絵を渡して「あとは頼んだ」とできれば便利かなと。
 clip2gifのsaveイベントはPICTデータを受け付けるようなので、それを使えばあっさりといけそうな感じ。ファイルに保存したい場合なら、保存したいPICT、保存形式、保存ファイルを指定するだけでとりあえずはokだ。具体的な流れとしては、clip2gifをターゲットとしてイベントクラスがkAECoreSuite、イベントIDがkAESaveのイベントを作成し、keyDirectObjectにPICTデータ(PicHandleの中身)、keyAEFileTypeにファイルタイプ、keyAEFileにファイルを指定するだけ。
 clip2gifが起動していない時はLaunchApplication()で起動してイベントを送る。この方法については2.19に書いたので省略。odocイベントの代わりにsaveイベントを送ると。

 単に保存してもらうだけならこれで終わりなのだが、いくつか設定があるのでそれも伝えてやらないといけない。インタレースの設定はBooleanだから簡単だが、問題は透過色だ。なしの場合は'no 'を渡すだけだが、「右下……」の場合はインデックスからRGBColorに変換してやる必要がある。逆に「選択色」の場合でmi-GIFで保存する時には、RGBColorからインデックスへの変換が必要になる。今回はそのあたりの話。
 最初、グラフポートのfgColorを書き換えることによって変換してもらえば楽かなと思っていたのだけど、どうもうまく動かない。256色のGWorldを指定してもだめ。ひょっとしたらGrafPtrにキャストしてからいけたのかもしれないけど、ややこしいので自分で変換ルーチンを書くことにした。
 前にも書いたかも知れないけど、システム256色は、216色+64色にわけることができる。216色の方が所謂Web Colorというやつで、MacとWindowsの256色で共通のもの。IconPartyの横長のパレットにある色。64色の方は、赤、緑、青、灰色の系列で、IconPartyの縦長のパレットにある色。216+64が256じゃないのが気になる人がいるかも知れないが、重複している色が24色あり、216+64-24=256というわけ。
 これらの色は、まず最初から215番目までに216色のうちの黒以外の色が並んでいて、そのあとに赤の緑、青、灰色の系列と続く。黒は一番最後にある。このことを元に変換ルーチンを書けば、

/* RGBColorからインデックスに変換 */
short RGBColorToIndex(RGBColor *color)
{
	short	r=0xf & (short)color->red,g=0xf & (short)color->green,
		b=0xf & (short)color->blue;
	
	if (r==0 && g==0 && b==0) return 255; /* 黒 */
	if (r%3==0 && g%3==0 && b%3==0) /* 216色 */
		return (5-b/3)+(5-g/3)*6+(5-r/3)*36;
	else
		if (g==0) /* red or blue */
			if (b==0) return 215+9-(r*2/3); /* red */
			else return 235+9-(b*2/3); /* blue */
		else /* green or gray */
			if (r==0) return 225+9-(g*2/3); /* green */
			else return 245+9-(r*2/3); /* gray */
}

/* インデックスからRGBColorに変換 */
void IndexToRGBColor(short index,RGBColor *color)
{
	if (index < 215)
		Set256RGBColor(color,(5-index/36)*3,(5-(index/6)%6)*3,
				(5-index%6)*3); /* 216 */
	else if (index < 225)
		Set256RGBColor(color,14-(index-215)*3/2,0,0); /* red */
	else if (index < 235)
		Set256RGBColor(color,0,14-(index-225)*3/2,0); /* green */
	else if (index < 245)
		Set256RGBColor(color,0,0,14-(index-235)*3/2); /* blue */
	else if (index < 255)
		Set256RGBColor(color,14-(index-245)*3/2,
		14-(index-245)*3/2,14-(index-245)*3/2); /* gray */
	else
		Set256RGBColor(color,0,0,0); /* black */
}

/* 色を設定 */
void Set256RGBColor(RGBColor *color,short r,short g,short b)
{
	color->red=0x1111U*r;
	color->green=0x1111U*g;
	color->blue=0x1111U*b;
}


こんな感じになるかな。Set256RGBColor()はIndexToRGBColor()の下請けルーチンで、0から15までの値を0x0000から0xFFFFに拡張してRGBColorに設定する。
 特にわかりにくいところはないと思うけど、「*2/3」は、「14,13,11,10,8,7,5,4,2,1」という数列を「9,8,7,6,5,4,3,2,1,0」に変換するためのもの。「*3/2」は、逆(正確には1少なくなるが)の変換と。もう少し整理できるかも知れないけど、考え方としてはこんな感じかな。

to September 4, 1999 ↑ to September index → to September 8, 1999