〜 グラフィック 〜
VRAMのアドレス

このTipsは実用的なものではなく、入門講座では記述し切れないVRAMの扱い方を説明しています。

まず言葉の説明から始めるとして、ココでいうVRAMとは、
HSPウィンドウ内の描画内容の元となるRGB各輝度を格納したバイナリテーブルを指します。
言わば、「HSPウィンドウ内全てをpget命令で順番に取得して配列変数に入れたもの」です。

いつも使う座標同様に左上を基点にして表示されている分だけ順番に格納されていればわかりやすいのですが、
このVRAMには初心者にはとっつきにくい次に記す3つの特徴があります。

	・画像左下を要素0として、右上の輝度情報まで横方向の順番で格納されている
	・フルカラー時における1ピクセルの要素格納順は「RGB」ではなく、「BGR」である
	・横幅が4の倍数ではないとき、4の倍数サイズになるようパディングされる

HSPに限られたことではなく、ウィンドウ内のピクセル情報はウィンドウ左下を基点に格納されています。
フルカラーは各輝度256段階(0〜255)の1つ1バイトで表現するので、1ピクセルで3バイト。
デフォルトサイズ(640ピクセル×480ピクセル)のウィンドウだと0バイト目から

	0バイト目		「座標(0,479)の青輝度」
	1バイト目		「座標(0,479)の緑輝度」
	2バイト目		「座標(0,479)の赤輝度」
	3バイト目		「座標(1,479)の青輝度」
	4バイト目		「座標(1,479)の緑輝度」
	5バイト目		「座標(1,479)の赤輝度」
	6バイト目		「座標(2,479)の青輝度」
	7バイト目		「座標(2,479)の緑輝度」
	8バイト目		「座標(2,479)の赤輝度」
	  :				:
	  :				:
	1917バイト目		「座標(639,479)の青輝度」
	1918バイト目		「座標(639,479)の緑輝度」
	1919バイト目		「座標(639,479)の赤輝度」
	1920バイト目		「座標(0,478)の青輝度」
	1921バイト目		「座標(0,478)の緑輝度」
	1922バイト目		「座標(0,478)の赤輝度」
	  :				:
	  :				:
	921597バイト目	「座標(639,0)の青輝度」
	921598バイト目	「座標(639,0)の緑輝度」
	921599バイト目	「座標(639,0)の赤輝度」

という感じで合計921600バイトが順に格納されています。
コレだけならまだしも、3つ目の特徴がわかりにくくてとっつきにくくしている一番の原因で、
例えばウィンドウサイズが641ピクセル×480ピクセルである場合は、
VRAMのサイズは合計923040(横幅641×高さ480×各輝度3)とはなってないのです。
詳細は割愛しますが1列分のメモリサイズは4の倍数になっている必要があるので、
横幅が641だと1列分が1923(横幅641×各輝度3)ではなく、
1列分が1924(横幅641×各輝度3+パディング)になって合計923520バイトとなっているのです。
つまり、

	0バイト目		「座標(0,479)の青輝度」
	1バイト目		「座標(0,479)の緑輝度」
	2バイト目		「座標(0,479)の赤輝度」
	3バイト目		「座標(1,479)の青輝度」
	4バイト目		「座標(1,479)の緑輝度」
	5バイト目		「座標(1,479)の赤輝度」
	6バイト目		「座標(2,479)の青輝度」
	7バイト目		「座標(2,479)の緑輝度」
	8バイト目		「座標(2,479)の赤輝度」
	  :				:
	  :				:
	1920バイト目		「座標(640,479)の青輝度」
	1921バイト目		「座標(640,479)の緑輝度」
	1922バイト目		「座標(640,479)の赤輝度」
	1923バイト目		パディングで1バイト分だけ補正
	1924バイト目		「座標(0,478)の青輝度」
	1925バイト目		「座標(0,478)の緑輝度」
	1926バイト目		「座標(0,478)の赤輝度」
	  :				:
	  :				:
	923517バイト目	「座標(640,0)の青輝度」
	923518バイト目	「座標(640,0)の緑輝度」
	923519バイト目	「座標(640,0)の赤輝度」

上記のように赤字の部分が追加されているのです。
横幅自体が4の倍数サイズになるという意味ではないので間違えないでください。
補正値を加えた1列分のメモリサイズの算出式は
「初期化済横幅×各輝度分3バイト+補正値」からHSP3だと「ginfo_sx * 3 + (ginfo_sx \ 4)」です。
昔からビット幅のバイトサイズを求める計算式として、
「((ginfo_sx * 24 + 31) & 0xFFFFFFE0) >> 3」でも求められますが、直感的にわかりにくいと思います。
何故この計算式で求められるか気になる方は各自で考えてください…。
HSPのマニュアルには「(ginfo_sx * 3 + 3) & 0xFFFFFFFC」と書かれています。
いずれも同じ答えになる式ですが、それぞれのやり方を100万回実行を10回繰り返した平均タイムは、
「ginfo_sx * 3 + (ginfo_sx \ 4)」が243ミリ秒、
「((ginfo_sx * 24 + 31) & 0xFFFFFFE0) >> 3」が192ミリ秒、
「(ginfo_sx * 3 + 3) & 0xFFFFFFFC」が181ミリ秒でしたのでHSPマニュアルのやり方を採用します。
「0xFFFFFFE0」はビット演算で31以外、「0xFFFFFFFC」は3以外を表しています。

尚、横幅をginfo_winx、高さをginfo_winyとして算出してないのは、
「現在表示されているウィンドウサイズ=正式なウィンドウサイズ」とは限らないから。
screen命令やbgscr命令、buffer命令で初期化したサイズ分だけ確保されているので、
width命令等で変更されている場合は正常なサイズを算出することができなくなります。
HSP3用の算出式を書きましたが、HSP2においてもwinxwinyではなく、
ginfo命令のタイプ6で取得できる初期化済ウィンドウサイズを使用するようにしてください。

1列分のメモリサイズを求めるだけでは何がしたいのかわからないので、
本題である「特定座標を示すVRAMのアドレス位置算出」について話を進めます。
VRAMデータはウィンドウ左下から始まってウィンドウ幅×3つの輝度で1列分、
ソレがウィンドウ高さ分用意されているので、例えばウィンドウ一番下の座標を表すアドレスは、
「X座標 * 3」から3バイト分が対象座標のデータとなりますね?
一番下から一列上の座標を表すアドレスだと先に書いた1列分を足せばよいのですが、
「((ginfo_sx * 3 + 3) & 0xFFFFFFFC) + (X座標 * 3)」から3バイト分ということになります。
Y座標の上下が逆転しているので、一番上の列にある任意の座標の場合は、
「(ginfo_sy - 1) * ((ginfo_sx * 3 + 3) & 0xFFFFFFFC) + (X座標 * 3)」となります。
以上を基にして、X座標・Y座標共にどこでも指定可能にしたのが下記のサンプルです。

尚、画像処理はとりわけ速度を要求される処理であり、
紹介モジュールをそのまま使うことはモジュール呼び出しに掛かるオーバーヘッド分だけムダになるので、
自分の組み込むVRAM処理内に組み込む形として使用することをお奨めします。
自分のスクリプトに組み込む際に気をつけなければならないことがあります。
各輝度分peek命令3回実行して1ピクセル分のデータ取り出す必要があり、
wpeek命令やlpeek命令を使うことで取得回数を減らすことが可能なわけですが、
ウィンドウ右端部を取り出す際にパディングデータも含めてしまうとエラーが出てしまうので、
右端のピクセル取得だけはpeek命令で取り出すようにしてください。
HSP2ならばll_peek命令を使うことで3バイト分だけ取り出すことが可能ですが、
変数のポインタを使うことになるのでll_getptr命令とll_ret命令でポインタを取得しなければなりません。
使い方についてはコチラをご覧ください。
	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#module
#deffunc pos2vram int, int
	mref x, 0 : mref y, 1
	mref stt, 64
	ginfo 6
	stt = (prmy - 1 - y) * ((prmx * 3 + 3) & 0xFFFFFFFC) + (x * 3)
	return
#global

	p = 0, 0
	pos2vram p, p.1
	mes "左上(" + p + ", " + p.1 + ")のVRAMは" + stat + "バイト目から3バイト分"
	p = winx - 1, 0
	pos2vram p, p.1
	mes "左上(" + p + ", " + p.1 + ")のVRAMは" + stat + "バイト目から3バイト分"
	p = 0, winy - 1
	pos2vram p, p.1
	mes "左上(" + p + ", " + p.1 + ")のVRAMは" + stat + "バイト目から3バイト分"
	p = winx - 1, winy - 1
	pos2vram p, p.1
	mes "左上(" + p + ", " + p.1 + ")のVRAMは" + stat + "バイト目から3バイト分"
	// 色の取り出し
	color 0xD1, 0xA5, 0x69
	p = 78, 90
	pset p, p.1
	pos2vram p, p.1 : adr = stat
	mref vram, 66
	peek r, vram, adr + 2 : str r, 16
	peek g, vram, adr + 1 : str g, 16
	peek b, vram, adr + 0 : str b, 16
	color
	pos p - 7, p.1 + 7
	mes "↑\n輝度(" + r + ", " + g + ", " + b + ")"
	stop
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#module
#defcfunc pos2vram int x, int y
	return (ginfo_sy - 1 - y) * ((ginfo_sx * 3 + 3) & 0xFFFFFFFC) + (x * 3)
#global

	p = 0, 0
	mes strf("左上(%3d, %3d)のVRAMは%dバイト目から3バイト分", p, p.1, pos2vram(p, p.1))
	p = ginfo_winx - 1, 0
	mes strf("右上(%3d, %3d)のVRAMは%dバイト目から3バイト分", p, p.1, pos2vram(p, p.1))
	p = 0, ginfo_winy - 1
	mes strf("左下(%3d, %3d)のVRAMは%dバイト目から3バイト分", p, p.1, pos2vram(p, p.1))
	p = ginfo_winx - 1, ginfo_winy - 1
	mes strf("右下(%3d, %3d)のVRAMは%dバイト目から3バイト分", p, p.1, pos2vram(p, p.1))
	// 色の取り出し
	color 0xD1, 0xA5, 0x69
	p = 78, 90
	pset p, p.1
	adr = pos2vram(p, p.1)
	mref vram, 66
	color
	pos p - 7, p.1 + 7
	mes strf("↑\n輝度(%X, %X, %X)", peek(vram, adr + 2), peek(vram, adr + 1), peek(vram, adr))