#author("2023-04-20T22:41:47+09:00","","")
* Tutorial.3 ビットマップモード(2) [#t3e84764]
#freeze
#author("2023-05-30T10:35:12+09:00","","")
* ビットマップモード2 [#c656504b]
先ほどのサンプルコードを変更して移動処理を加えてみます。

** 応用してみよう [#x7b387ed]
1ドットごとに点を打つことができるようになりましたので~
いろいろな描画ができるようになると思います。~

 // モード3で四角形を塗りつぶす
 void Mode3DrawBox(u32 sx, u32 sy, u32 ex, u32 ey, u16 col)
 int main(void)
 {
 	u32 x, y;
 	
 	for(y=sy; y<ey; y++)
 	SetMode(MODE_3 | BG2_ENABLE);
 
 	s32 x=0, y=0;
 
 	for(;;)
 	{
 		for(x=sx; x<ex; x++)
 		if(y > 0 && x > 0)
 		{
 			Mode3PutPixel(x, y, col);
 			Mode3PutPixel(x-1, y-1, RGB5(0, 0, 0));
 		}
 
 		Mode3PutPixel(x, y, RGB5(31, 31, 31));
 
 		x++;
 		y++;
 
 		if(y > 160)
 		{	
 			x = 0;
 			y = 0;
 		}
 	}
 }

変数xとyの座標には白色ドット、1つ前に描画したドットには黒色(背景色)で上書きしています。x, y共にインクリメントしているので、どのように表示されていくかは想像できると思います。ただし、残念ながらこの書き方では想像どおりの結果になりません。失敗した状態をぜひ自分の目で確かめてください。ダウンロードはこちらです。

他にも、直線を引いたり、円や三角形を描画することもできます。~
直線の描画は、有名なブレゼンハムのアルゴリズムなどを検索してみてください。~
CGプログラムの本などを見てアルゴリズムを学ぶのもいいと思います。~
#ref(tut03 bitmap2_test_nowait.gba)

** 垂直同期待ちの処理を加える [#iedf4218]

** png画像をGBAで表示する [#cb9ffcae]
次に、画像ファイルをGBA上に表示させてみる関数を考えてみます。~
画像は液晶画面のサイズに合わせた240x160ドットなので、内容は以下のようになります。~
失敗からわかることは、点が滑らかに表示されておらずブツ切りにされています。結論から言ってしまうと垂直同期待ちの処理をしていないからです。GBAは高速で画面を書き換えています。具体的には左上0,0座標から右へ順番にドット単位で行い、その行が終わったら次の行へ、という具合です。

 void Mode3DrawImage(u16* img)
#ref(1.png,nolink)

もう少し詳しく言い直しましょう。液晶画面は240x160ドットです。最初のラインは240ドット描画後、描画しない68ドット分が存在します。この非描画期間をH-Blankといいます。この横ラインが完了したら次の横ラインへ、(0,1)ドット目からとなります。縦160ライン分終わった後にも縦68ライン分存在します。この非描画期間をV-Blankといいます。

- Horizontal Dimensions (H-Blank)
 The drawing time for each dot is 4 CPU cycles.
 
  Visible     240 dots,  57.221 us,    960 cycles - 78% of h-time
  H-Blanking   68 dots,  16.212 us,    272 cycles - 22% of h-time
  Total       308 dots,  73.433 us,   1232 cycles - ca. 13.620 kHz

- Vertical Dimensions (V-Blank)
  Visible (*) 160 lines, 11.749 ms, 197120 cycles - 70% of v-time
  V-Blanking   68 lines,  4.994 ms,  83776 cycles - 30% of v-time
  Total       228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz

描画処理を行う場合、基本的にはV-Blankの間に行います。先ほどのサンプルコードはV-Blankを無視して描画した為、チラついて見えたりしました。V-Blankを調べるには以下のレジスタを参照します。

 4000006h - VCOUNT - Vertical Counter (Read only)
 Indicates the currently drawn scanline, values in range from 160..227 indicate 'hidden' scanlines within VBlank area.
 
   Bit   Expl.
   0-7   Current Scanline (LY)      (0..227)                              (R)
   8     Not used (0) / NDS: MSB of Current Scanline (LY.Bit8) (0..262)   (R)
   9-15  Not Used (0) 

 void WaitForVsync(void)
 {
 	u16* ScreenBuffer = (u16*)0x6000000;
 	u16 x, y;
 	
 	for(y=0; y<160; y++)
 	{
 		for(x=0; x<240; x++)
 		{
 			ScreenBuffer[y*240+x] = img[y*240+x];
 		}
 	}
 	while(*(vu16*)0x4000006 >= 160) {};
 	while(*(vu16*)0x4000006 <  160) {};
 }

WaitForVsync関数は、VCOUNT(描画中のライン)が160になるまで待つことを意味しています。先ほどのコードに加えた違いを確認してください。結果が良好になることがわかると思います。

引数の画像データ(色データ)の配列から1つ1つを描画をしていきます。~
この配列をどのように生成するかですが、devkitProに標準装備されている''grit''というツールを~
使用して出力するのが手っ取り早いです。使い方は[[Doc.3 画像加工ツールの使い方>doc.3]]を参照してください。~


** モード3での画像表示 [#l814af10]
** 流れ星っぽいドット表示 [#md5c1708]
 #include "lib/gba.h"
 #include "res.h"
 
 //---------------------------------------------------------------------------
 void WaitForVsync(void)
 {
 	while(*(vu16*)0x4000006 >= 160) {};
 	while(*(vu16*)0x4000006 <  160) {};
 }
 //---------------------------------------------------------------------------
 void Mode3DrawImage(u16* img)
 void Mode3PutPixel(u32 x, u32 y, u16 color)
 {
 	u16* ScreenBuffer = (u16*)0x6000000;
 	u32  x, y;
 
 	for(y=0; y<160; y++)
 	{
 		for(x=0; x<240; x++)
 		{
 			ScreenBuffer[y*240+x] = img[y*240+x];
 		}
 	}
 	u16* ScreenBuffer     = (u16*)0x6000000;
 	ScreenBuffer[y*240+x] = color;
 }
 //---------------------------------------------------------------------------
 int main(void)
 {
 	// モード設定
 	SetMode(MODE_3 | BG2_ENABLE);
 
 	// 画像の読み込み
 	Mode3DrawImage((u16*)&imageBitmap);
 	s32 x=0, y=0;
 
 	for(;;)
 	{
 	    WaitForVsync();
 		WaitForVsync();
 
 		if(y != 0 || x != 0)
 		{
 			Mode3PutPixel(x-1, y-1, RGB5(0, 0, 0));
 		}
 
 		Mode3PutPixel(x++, y++, RGB5(31, 31, 31));
 
 		if(y > 160)
 		{	
 			x = 0;
 			y = 0;
 		}
 	}
 }


***動作画面 [#m958e69b]
モード3で、240x160のpng画像の表示
#ref(clip_1.png,nolink)
#ref(2.png,nolink)


** 履歴 [#p6ae87c6]
- 2014/12/21
- 2023/04/22


トップ   一覧 検索 最終更新   ヘルプ   最終更新のRSS