・ビットマップの回転(2) (99/11/13)

前回から結構間が空いてしまいましたが、これは更新を忘れていたから ではなくて…、いや、忘れてました (^^;;

今回は VOID DrawPolygon() のテクスチャ画像の「斜め読み」 の部分を固定少数点化して高速化を行います。と言っても、 基本的にやる事は同じで、違うのは

double tx,ty,tdx,tdy; // テクスチャ座標計算用

となっていた部分を

WORD tx,ty,tdx,tdy;

の様に double 型ではなくて WORD 型に変えて、後は 少数点をシフト演算で右にずらして(<<)足し算していくだけです。 まあ、具体的には下のソースを見れば分かると思います。 VOID DrawPolygon() 以外の関数は前回と同じです。

さて、どのくらい速くなるかですが、前回は MMX-P155 の マシンでは 22 FPS でしたが、今回は 82 FPS まで速くなりました(^^。 ただし、(当然ですが)精度が 16 bit になったため画像は荒くなります。

ちなみにエッジのスキャンをする VOID ScanLine() も固定少数化 すると少しは速くなりますが、ループ数が小さいので劇的には 速くならず、逆にエッジがガタガタになるので止めた方が良いです。


//--------------------------------------------
// ポリゴン描画関数 16bit 固定少数使用版 
// 小数点第一桁の位置は右から 5 bit 目、つまり整数部は +-1024 の範囲
// この範囲を超えないように気を付けること 
//
// Y 軸スキャン  固定少数使用で平均 82 FPS

VOID DrawPolygon(LPPOLYDATA lpPolyData,  // ポリゴンデータ
                 LPSCANDATA lpScanData,  // Y 軸スキャン用データのバッファ
                 LPBYTE lpDest,  // 描画画面のバッファ
                 LONG nDestWidth, // 描画座標幅
                 LONG nDestHeight, // 描画座標高さ
                 LPBYTE lpTex, // テクスチャのバッファ
                 LONG nTexWidth, // テクスチャ幅
                 LONG nTexHeight // テクスチャ高さ

        ){
	
	LONG x,y; // ループ用
	LONG nStartY,nEndY; // 描画開始を開始する Y 座標、終了座標
	
	LONG i,i2;
	LONG maxX,minX;  // エッジ座標の最大最小値

	DWORD dwTexSize = (DWORD)(nTexHeight*nTexWidth); // テクスチャのサイズ
	DWORD dwReadPoint; // テクスチャデータを読み込むアドレス
	DWORD dwScanData,dwDest; // エッジの座標データと転送先のベースアドレス
	
	// エッジの座標のスキャン
	ScanLine(lpPolyData,lpScanData,nDestWidth,nDestHeight,&nStartY,&nEndY);
	
	// 範囲外なら描画しない
	if(nStartY >= nDestHeight || nEndY < 0) return;
	
	// 転送先バッファとスキャンデータのベースアドレスセット
	// 転送先バッファ(DIB)は上下が反転しているのに注意((nDestHeight-1-nStartY)の所)
	dwDest = (DWORD)lpDest + (DWORD)((nDestHeight-1-nStartY)*nDestWidth);
	dwScanData = (DWORD)lpScanData + (DWORD)(nStartY*sizeof(SCANDATA));
	
	// nStartY から nEndY まで上から順に描画
	for(y = nStartY; y <= nEndY; y++){
	
	// エッジの(転送先画像(dwDest)上での)座標セット
	minX = *(LPLONG)(dwScanData);
	maxX = *(LPLONG)(dwScanData+4);

	if(maxX >= 0){
	
	// テクスチャ座標計算用変数(16 bit 固定少数)
	WORD tx,ty,tdx,tdy; 
	
	// 初期テクスチャ座標セット (minTx,minTy) -> (tx,ty) 
	// 及び小数点の位置移動 (5 bit 分右に)
	memcpy(&tx,(VOID*)(dwScanData+8),sizeof(WORD));
	memcpy(&ty,(VOID*)(dwScanData+12),sizeof(WORD));
	tx <<= 5; 
	ty <<= 5;
	
	// 描画座標で x  が 1 増加した時のテクスチャ座標での
	// x と y の変位を計算
	if(maxX != minX){ 
	
	i2 = (maxX-minX)<<16; // DWORD -> WORD 変換
	
	// x の変位 tdx = (maxTx-minTx)/(maxX-minX)
	// 全体で見ると ( << 21 / << 16) = << 5 なので 5 bit 右へ小数点移動
	i = ((*(LPLONG)(dwScanData+16)-*(LPLONG)(dwScanData+8))<<21)/i2; 
	memcpy(&tdx,&i,sizeof(WORD));
	
	// y の変位 tdy = (maxTy-minTy)/(maxX-minX)
	i = ((*(LPLONG)(dwScanData+20)-*(LPLONG)(dwScanData+12))<<21)/i2;
	memcpy(&tdy,&i,sizeof(WORD));
	
	}
	else { // 一点の時
	tdx = 0;
	tdy = 0;
	}
	
	// 左側エッジ(minX)が 0 より小さいなら 0 になるまで回す
	while(minX < 0){
	minX++;
	tx += tdx;
	ty += tdy;
	}
	
	// 左から右に横に描画していく
	maxX = min(nDestWidth-1,maxX);
	for(x = minX; x <= maxX; x++){
	
	// テクスチャデータを読み込む点を計算
	dwReadPoint = (DWORD)(nTexHeight-1-(ty>>5))*nTexWidth+(tx>>5);
	
	// コピー
	if(dwReadPoint < dwTexSize) *(LPBYTE)(dwDest+x) = lpTex[dwReadPoint];
	
	// テクスチャ座標移動
	tx += tdx;
	ty += tdy;
	}
	}
	
	// バッファとスキャンデータのベースアドレス更新
	dwDest -= nDestWidth;
	dwScanData += sizeof(SCANDATA);
	}
}