**Doc.18 日本語を表示する方法(タイルモード その2) [#e704fe29]
タイルモードの8x8ドットでは日本語表示は厳しいけど、16x16ドットにしてみたところ、逆にあまりに大きすぎてガッカリされてしまったことはないでしょうか。そもそもタイルモードは、8x8〜16x16ドット以内の微妙なサイズを表現するには不向きです。もし仮にビットマップモードみたいに1ドットづつ描画をしたい場合、苦労される点としては描画始点位置のアドレス計算ではないかと思います。今回は慎重に作業を進めるため、メモ書き程度の計画案を考えてみるところから始めました。


** 案を考える [#kf0e9b05]
とりあえずGBAの仕様を考慮しつつ、3つほど考えてみます。一番作っていく上でラクなの&見栄えがいいのはどれなのか、ちょっと考えてみてください。[[案1〜3の図解:http://akkera102.sakura.ne.jp/test/clip_1.png]]


- 案1 12x10ドット(余白:0x0ドット)~
- 案2 12x10ドット(余白:0x2ドット)~
- 案3 12x14ドット(余白:0x2ドット)~


ここでわかることは、1番目、2番目はポインタを4個用意し、2行目以降も位置がバラバラなので計算が恐ろしく大変になることがわかります。もし作るとしたら、Excelかpythonなどでテーブルを予め用意した方がいいかもしれません。3番目はx座標が偶数か奇数だけ処理が分かれそうなので比較的簡単そうです。何回もトライ&エラーを繰り返して、以下のソースコードを作ることに成功しました。


 //---------------------------------------------------------------------------
 EWRAM_CODE void BgFontDrawChr(u16 x, u16 y, u16 chr)
 {
 	u16 idx = BgFontGetIdx(chr);
 	u16 i;
 
 	u16* s1 = BgFont.pDat + idx * BG_FONT_DAT_SIZE / 2;
 	u16* s2 = s1 + 32 / 2;
 	u16* s3 = s2 + 32 / 2;
 	u16* s4 = s3 + 32 / 2;
 
 	if(x & 0x1)
 	{
 		u16* d1 = Bg[1].tileBaseAdr + y*1024 + (((((x+1)/2)*3)-2) * 32 / 2) + 1;
 		u16* d2 = d1 + 15;
 		u16* d3 = d1 + 512;
 		u16* d4 = d2 + 512;
 
 		for(i=0; i<8; i++)
 		{
 			*d1++ = *s1++;
 			d1++;
 
 			*d2++ = *s1++;
 			*d2++ = *s2++;
 			s2++;
 		}
 
 		for(i=0; i<6; i++)
 		{
 			*d3++ = *s3++;
 			d3++;
 
 			*d4++ = *s3++;
 			*d4++ = *s4++;
 			s4++;
 		}
 	}
 	else
 	{
 		u16* d1 = Bg[1].tileBaseAdr + y*1024 + ((x/2)*3) * 32 / 2;
 		u16* d2 = d1 + 32 / 2;
 		u16* d3 = d1 + 512;
 		u16* d4 = d3 + 32 / 2;
 
 		for(i=0; i<8; i++)
 		{
 			*d1++ = *s1++;
 			*d1++ = *s1++;
 
 			*d2++ = *s2++;
 			d2++;
 			s2++;
 		}
 
 		for(i=0; i<6; i++)
 		{
 			*d3++ = *s3++;
 			*d3++ = *s3++;
 
 			*d4++ = *s4++;
 			d4++;
 			s4++;
 		}
 	}
 }


いやーむっちゃカオスなコードですがツッコミはご勘弁ください(泣。


** フォントのインデックス [#leecc924]
次に以下のような呼び出しがあったとき、フォントのインデックスを求める計算を考えみます。


 BgFontDrawStr(0, 1, " 123あいう亜意兎★(〜_〜");


ここではSJISコードを採用しているので、それに合わせないといけません。SJISコード表を見ていただけるとわかるとおり、コード間の途中に空白があるのでプログラムを作るには工夫がいります。今まではフォントシートと呼ばれるテーブルを用意して計算していましたが、サイズが53962バイト(" "〜"熙"まで。1フォントあたり2バイト)というトンデモない大きさになってしまいました。


 // 昔のコード
 EWRAM_CODE u16 BgFontGetIdx(u16 sjis)
 {
 	u16 cnt = sjis - 0x8140;     //0x8140はSJISの先頭文字(' ')
 
 	return (cnt >= FONT_SHT_MAX_CNT) ? 0 : BgFont.pSht[cnt];
 }


これでは無駄が多いということで、yasuhoさんの[[GBAファイラー:http://www.geocities.jp/yasuho68k/]]の計算方法をそのまま流用させていただくことになりました。テーブルサイズは1408バイトという、驚異のダイエットに成功しています(^^;。ポイントはSJISコードを3:5:2:6ビットに分解して、3段階のインデックステーブルにすることです。この為、インデックス(3:5:2ビット)+開始から終点(6ビット)までの情報を記録するだけで済んでいます。


 #define BG_FONT_INVALID_INDEX	5	// エラー時のインデックスコード "・"
 
 
 typedef struct {
 	u8 sig[2];
 	u8 ver;
 	u8 size;
 	u8 index;
 	u8 reserved[11];
 } __PACKED ST_CCT_HEAD;
 
 
 typedef struct {
 	u8 count;
 	u8 reserved[3];
 } __PACKED ST_XCCTENT;
 
 
 typedef struct {
 	u8  start;
 	u8  end;
 	u16 offset;
 } __PACKED ST_XCCT;
 
 
 typedef struct {
 	u16* pDat;	// フォントデータ
 	u8*  pCct;	// フォントシート
 } ST_BG_FONT;
 
 
 //---------------------------------------------------------------------------
 EWRAM_CODE u16 BgFontGetIdx(u16 code)
 {
 	if(_IsSJIS(HIBYTE(code)) == FALSE)
 	{
 		return BG_FONT_INVALID_INDEX;
 	}
 
 
 	// level 1 ---------------------------------
 	u16 c0 = HIBYTE(code) >> 5;
 	u16 c1 = HIBYTE(code) & 0x1f;
 	u16 i1;
 
 	if(c0 == 4)
 	{
 		// 80-9F
 		i1 = ((u16*)BgFont.pCct)[c1];
 	}
 	else
 	{
 		// E0-FF
 		i1 = ((u16*)BgFont.pCct)[c1 + 32];
 	}
 
 	if(i1 == 0)
 	{
 		return BG_FONT_INVALID_INDEX;
 	}
 
 
 	// level 2 ---------------------------------
 	u16 c2 = LOBYTE(code) >> 6;
 	u16 i2 = ((u16*)(BgFont.pCct + i1))[c2];
 
 	if(i2 == 0)
 	{
 		return BG_FONT_INVALID_INDEX;
 	}
 
 	ST_XCCTENT* pXccTent = (ST_XCCTENT*)(BgFont.pCct + i2);
 	ST_XCCT*    pXcct    = (ST_XCCT*)(BgFont.pCct + i2 + sizeof(ST_XCCTENT));
 
 
 	// level 3 ---------------------------------
 	u16 c3 = LOBYTE(code) & 0x3f;
 	u16 i;
 
 	for(i=0; i<pXccTent->count; i++)
 	{
 		if(c3 >= pXcct->start && c3 <= pXcct->end)
 		{
 			return pXcct->offset + (c3 - pXcct->start);
 		}
 
 		pXcct++;
 	}
 
 	return BG_FONT_INVALID_INDEX;
 }


とりあえず細かい話はダウンロードしつつ、コードを読んでいただければ幸いです。


** ttf2bmp [#l80d6a1b]
最後に、今回はbdfを使わずフォントに丸みのあるttf形式を採用しました。bmp生成にはpython+pygameを使用し、サーフェイスに1文字づつ描画した後、スクリーンキャプチャの要領で保存をしています。


 # -*- coding:sjis -*-
 import pygame
 import codecs
 import os
 
 
 def main():
     # フォントコードが書かれたテキストを読み込みます
     file = codecs.open('font_idx.txt', 'r', 'utf_16')
     str = file.read()
     file.close()
 
 
     # サーフェイスの大きさを定義します
     str_len = len(str)
     cy = (((str_len * 16) / 256) + 1) * 16
 
     pygame.init()
     scn = pygame.Surface((256, cy))
     scn.fill((255, 255, 255))
 
 
     # ttfフォントを読み込みます
     font = pygame.font.Font("みかちゃん.ttf", 12)
 
 
     # 1文字づつ、サーフェイスに書き込んでいきます
     x = 0
     y = 0
     for i in range(str_len):
         sur = font.render(str[i], True, (0,0,0))
         scn.blit(sur, (x,y))
         x = x + 16
         if x >= 256:
             x = 0
             y = y + 16
 
     # サーフェイスを保存します
     pygame.image.save(scn, "font_mika.bmp")
     return
 
 
 if __name__ == '__main__': main()
 # end of file


#ref(clip_2.png,nolink)


** 12x14ドット(余白:0x2ドット)の日本語表示例 [#mf27bc93]
 #include "lib/gba.h"
 #include "bg.h"
 
 
 //---------------------------------------------------------------------------
 void WaitForVsync()
 {
 	while (*(volatile u16*)0x4000006 >= 160) {};
 	while (*(volatile u16*)0x4000006 <  160) {};
 }
 //---------------------------------------------------------------------------
 int main()
 {
 	BgInit();
 	BgFontInit();
 
 	BgAsciiDrawStr(0, 0, "ASCII FONT TEST");
 	BgFontDrawStr(0, 1, " 123あいう亜意兎★(〜_〜");
 
 
 	for(;;)
 	{
 	    WaitForVsync();
 	}
 }


*** 動作画面 [#pae124f2]
#ref(clip_1.png,nolink)


** 履歴 [#s1332b55]
- 2008/05/26