ホーム < ゲームつくろー! < DirectX技術編 < その35 高速フォント表示(アンチエイリアス無しバージョン):サンプルプログラム


その35 高速フォント表示(アンチエリアス無しバージョン):サンプルプログラム


 DirectX技術編その35「高速フォント表示(アンチエリアス無しバージョン)」で説明した内容を踏まえたサンプルプログラムです。実行すると、ブルースクリーンのバックにアンチエイリアスがかかっていない明朝体の「あ」が出現します。

 このサンプルでは32ピクセルの明朝体「あ」をどうサイズのテクスチャに貼り付け、それを5倍に拡大してあります。また、少しだけ透過度を入れていますので背景の青色が透けています。


 以下のプログラムは空のプロジェクトにコピペすることで完全に動きます。上からデバッグしていくと、何をしているか良くわかるように1つのソースで完結させています。今回のポイントになりそうなところは太文字で示されています。

(もしうまく動かないようでしたら掲示板にてご連絡下さい)

// 高速フォント表示サンプルプログラム
// (アンチエイリアス無しモノトーンバージョン)
   
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "d3d9.lib")

#include <windows.h>
#include <tchar.h>
#include <d3d9.h>
#include <d3dx9.h>

TCHAR gName[100] = _T("高速フォント表示サンプルプログラム");


/// 頂点関係 ///
#define FVF_CUSTOM ( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 ) // 座標変換済み頂点

struct CUSTOMVERTEX{
   float x,y,z; // 頂点座標 
   float rhw; // 除算数
   DWORD dwColor; // 頂点の色
   float u, v; // テクスチャ座標 
};



LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam){
   if(mes == WM_DESTROY) {PostQuitMessage(0); return 0;}
   return DefWindowProc(hWnd, mes, wParam, lParam);
}


int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
   // アプリケーションの初期化
   MSG msg; HWND hWnd;
   WNDCLASSEX wcex ={sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, NULL, NULL,
                                    (HBRUSH)(COLOR_WINDOW+1), NULL, (TCHAR*)gName, NULL};
   if(!RegisterClassEx(&wcex))
      return 0;

   if(!(hWnd = CreateWindow(gName, gName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
                                    NULL, NULL, hInstance, NULL)))
      return 0;

   // Direct3Dの初期化
   LPDIRECT3D9 g_pD3D;
   LPDIRECT3DDEVICE9 g_pD3DDev;
   if( !(g_pD3D = Direct3DCreate9( D3D_SDK_VERSION )) ) return 0;

   D3DPRESENT_PARAMETERS d3dpp = {0,0,D3DFMT_UNKNOWN,0,D3DMULTISAMPLE_NONE,0,
                                                      D3DSWAPEFFECT_DISCARD,NULL,TRUE,0,D3DFMT_UNKNOWN,0,0}; 

   if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                                   D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDev ) ) ){
   g_pD3D->Release();
   return 0;
   }

   ShowWindow(hWnd, nCmdShow);

   // フォントの生成
   int fontsize = 32;
   LOGFONT lf = {fontsize, 0, 0, 0, 0, 0, 0, 0, SHIFTJIS_CHARSET, OUT_TT_ONLY_PRECIS,
   CLIP_DEFAULT_PRECIS, PROOF_QUALITY, FIXED_PITCH | FF_MODERN, _T("MS 明朝")};
   HFONT hFont;
   if(!(hFont = CreateFontIndirect(&lf))){
      g_pD3DDev->Release(); g_pD3D->Release();
      return 0;
   }

   // デバイスコンテキスト取得
   // デバイスにフォントを持たせないとGetGlyphOutline関数はエラーとなる
   HDC hdc = GetDC(NULL);
   HFONT oldFont = (HFONT)SelectObject(hdc, hFont);

   // 文字コード取得
   TCHAR *c = _T("あ");
   UINT code = 0;
#if _UNICODE
   // unicodeの場合、文字コードは単純にワイド文字のUINT変換です
     code = (UINT)*c;
#else
   // マルチバイト文字の場合、
   // 1バイト文字のコードは1バイト目のUINT変換、
   // 2バイト文字のコードは[先導コード]*256 + [文字コード]です
   if(IsDBCSLeadByte(*c))
      code = (BYTE)c[0]<<8 | (BYTE)c[1];
   else
      code = c[0];
#endif

   // フォントビットマップ取得
   // GGO_BITMAPの場合、「DWORDにアラインメントされたモノトーンBMP」が返る
   // モノトーンBMPとは1ピクセルを1ビットで表現するビットマップの事
   TEXTMETRIC TM;
   GetTextMetrics( hdc, &TM );
   GLYPHMETRICS GM;
   CONST MAT2 Mat = {{0,1},{0,0},{0,0},{0,1}};
   DWORD size = GetGlyphOutline(hdc, code, GGO_BITMAP, &GM, 0, NULL, &Mat);
   BYTE *ptr = new BYTE[size];
   GetGlyphOutline(hdc, code, GGO_BITMAP, &GM, size, ptr, &Mat);

   // デバイスコンテキストとフォントハンドルの開放
   SelectObject(hdc, oldFont);
   DeleteObject(hFont);
   ReleaseDC(NULL, hdc);


   // 頂点情報
   float a = 10.0f; // テクスチャの縮尺
   float fTexW = GM.gmCellIncX * a; // テクスチャの横幅
   float fTexH = TM.tmHeight * a; // テクスチャの高さ
   DWORD FontColor = 0xaaffffff; // テクスチャカラー(透明度は0xaa)

   CUSTOMVERTEX v[]=
   {
      { fTexW, 0.0f, 0.0f, 1.0f, FontColor, 1.0f, 0.0f},
      { fTexW, fTexH, 0.0f, 1.0f, FontColor, 1.0f, 1.0f}, 
      { 0.0f, 0.0f, 0.0f, 1.0f, FontColor, 0.0f, 0.0f}, 
      { 0.0f, fTexH, 0.0f, 1.0f, FontColor, 0.0f, 1.0f} 
   };

   // 頂点バッファ作成
   IDirect3DVertexBuffer9* pVertex;
   if(FAILED(g_pD3DDev->CreateVertexBuffer(sizeof(CUSTOMVERTEX)*4, D3DUSAGE_WRITEONLY, FVF_CUSTOM,
                                                             D3DPOOL_MANAGED, &pVertex, NULL))){
      g_pD3DDev->Release(); g_pD3D->Release(); delete[] ptr;
      return 0;
   }

   // 頂点情報の書き込み
   void *pData;
   if(FAILED(pVertex->Lock(0, sizeof(CUSTOMVERTEX)*4, (void**)&pData, 0))){
      g_pD3DDev->Release(); g_pD3D->Release(); delete[] ptr;
      return 0;
   }
   memcpy(pData, v, sizeof(CUSTOMVERTEX)*4);
   pVertex->Unlock();


   // テクスチャ作成
   LPDIRECT3DTEXTURE9 pTex;
   if(FAILED(g_pD3DDev->CreateTexture( GM.gmCellIncX, TM.tmHeight, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8,
                                                    D3DPOOL_DEFAULT, &pTex, NULL))){
      g_pD3DDev->Release(); g_pD3D->Release(); delete[] ptr;
      return 0;
   }

   // テクスチャにフォントビットマップ書き込み
   D3DLOCKED_RECT LockedRect;
   if(FAILED(pTex->LockRect(0, &LockedRect, NULL, D3DLOCK_DISCARD))){
      g_pD3DDev->Release(); g_pD3D->Release(); delete[] ptr;
      return 0;
   }

   // フォント情報の書き込み
   // 取得したビットが1ピクセルに相当するので、
   // iOfs_x, iOfs_y : 書き出し位置(左上)
   // iBmp_w, iBmp_h : フォントビットマップの幅高
   int iOfs_x = GM.gmptGlyphOrigin.x;                        // テクスチャ書き込み位置のオフセット横位置
   int iOfs_y = TM.tmAscent - GM.gmptGlyphOrigin.y;          // テクスチャ書き込み位置のオフセット横位置
   int iBmp_w = GM.gmBlackBoxX;                              // 取得したBMPの横ピクセル数
   int iBmp_h = GM.gmBlackBoxY;                              // BMPの縦ピクセル数
   int iUseBYTEparLine = (1 + (GM.gmBlackBoxX / 32))*4;      // 1行に使用しているBYTE数(4バイトアラインメント)
   int x, y;                                                 // テクスチャの書き込み位置
   DWORD Alpha, Color;
   FillMemory(LockedRect.pBits , LockedRect.Pitch * TM.tmHeight, 0);
   for(y=iOfs_y; y<iOfs_y+iBmp_h; y++)
   for(x=iOfs_x; x<iOfs_x+iBmp_w; x++){
      DWORD num = (x-iOfs_x) / 8;                           // 現在のxが1行の何BYTE目かを算出
      BYTE bit = (x-iOfs_x) % 8;                            // 現在のxが1バイト内の何ビット目かを算出
      BYTE mask = ((BYTE)1)<<(7-bit);                       // 現在のビット数のマスク作成
      BYTE Cur = *((BYTE*)ptr + iUseBYTEparLine*(y-iOfs_y)+num);      // 現在のバイト位置にあるビットマップをを取得
      Cur &= mask;                                          // 作成したマスクと現在のバイト位置とで論理積演算
      Alpha = (Cur >> (7-bit)) * 255;                       // Curに立ったビットフラグを最下位ビットまでシフトしてアルファ値に変換
     Color = 0x00ffffff | (Alpha<<24);                      // アルファ値を登録

     // テクスチャに書き込み
     memcpy((BYTE*)LockedRect.pBits + LockedRect.Pitch*y + 4*x, &Color, sizeof(DWORD));
   }
   pTex->UnlockRect(0);
   delete[] ptr;

   // テクスチャセット
   g_pD3DDev->SetTexture(0, pTex);
   g_pD3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); 
   g_pD3DDev->SetTextureStageState(0, D3DTSS_COLOROP , D3DTOP_MODULATE );
   g_pD3DDev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); 
   g_pD3DDev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
   g_pD3DDev->SetTextureStageState(0, D3DTSS_ALPHAOP , D3DTOP_MODULATE ); 
   g_pD3DDev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE ); // 板ポリのα値を利用

   // レンダリングステート
   g_pD3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
   g_pD3DDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
   g_pD3DDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 


   // メッセージ ループ
   do{
      Sleep(1);
      if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ){ DispatchMessage(&msg);}
      else{
         // Direct3Dの処理
         g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
         g_pD3DDev->BeginScene();

            // 描画
            g_pD3DDev->SetStreamSource(0, pVertex, 0, sizeof(CUSTOMVERTEX));
            g_pD3DDev->SetFVF(FVF_CUSTOM);
            g_pD3DDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

         g_pD3DDev->EndScene();
         g_pD3DDev->Present( NULL, NULL, NULL, NULL );
      }
   }while(msg.message != WM_QUIT);

   pVertex->Release();
   pTex->Release();
   g_pD3DDev->Release();
   g_pD3D->Release();

   return 0;
}