BREWアプリを Windows へ移植(8)
IDisplay の実装について。
IDisplay はテキスト表示とかフォントとかありますが、その辺はとりあえず無視して、IDISPLAY_Update() や IDISPLAY_SetDestination(), IDISPLAY_GetDestination(), それから IDISPLAY_CreateDIBitmap() を実装していきます。
まずはヘッダの中身から。
class WinDisplay : public IDisplay{ public: WinDisplay(); ~WinDisplay(); private: static uint32 AddRef(IDisplay*); static uint32 Release(IDisplay*); static int GetFontMetrics(IDisplay * po,AEEFont nFont,int * pnAscent,int * pnDescent); static int MeasureTextEx(IDisplay * po, AEEFont nFont, const AECHAR * pcText,int nChars,int nMaxWidth, int * pnFits); static int DrawText(IDisplay * po,AEEFont nFont, const AECHAR * pcText,int nChars,int x,int y,const AEERect * prcBackground,uint32 dwFlags); static void DrawRect(IDisplay * po,const AEERect * pRect,RGBVAL clrFrame, RGBVAL clrFill, uint32 dwFlags); static void BitBlt(IDisplay * po,int xDest,int yDest,int cxDest,int cyDest,const void * pbmSource,int xSrc,int ySrc,AEERasterOp dwRopCode); static void Update(IDisplay * po, boolean bDefer); static void SetAnnunciators(IDisplay * pd, uint16 wVal, uint16 wMask); static void Backlight(IDisplay * po,boolean bOn); static RGBVAL SetColor(IDisplay * po, AEEClrItem clr, RGBVAL rgb); static AECHAR GetSymbol(IDisplay * po,AEESymbol sym, AEEFont nFont); // Helper routines - buttons, frames static int DrawFrame(IDisplay * po, AEERect * prc,AEEFrameType ft, RGBVAL rgbFill); static int CreateDIBitmap(IDisplay *po, IDIB **ppIDIB, uint8 colorDepth, uint16 w, uint16 h); static int SetDestination(IDisplay *po, IBitmap *pDst); static IBitmap* GetDestination(IDisplay *po); static int GetDeviceBitmap(IDisplay *po, IBitmap **ppIBitmap); static IFont* SetFont(IDisplay *piDisplay, AEEFont nFont, IFont *piFont); static void SetClipRect(IDisplay *piDisplay, const AEERect * pRect); static void GetClipRect(IDisplay *piDisplay, AEERect * pRect); static int Clone(IDisplay *po, IDisplay **ppIDisplayNew); static void MakeDefault(IDisplay *po); static boolean IsEnabled(IDisplay *po); static int NotifyEnable(IDisplay *po, AEECallback *pcb); private: IDisplayVtbl _vtbl; uint32 _ref; IBitmap* _dest; };
_dest は、このディスプレイにセットされている IBitmap です。
デフォルトではデバイスのビットマップがセットされています。
デバイスのビットマップはアプレット内で単一なので、WinApplication が保持しています。
なので、SetDestination(), GetDestination(), GetDeviceBitmap() は次のようになります。
int WinDisplay::SetDestination(IDisplay *po, IBitmap *pDst){ if( ((WinDisplay*)po)->_dest == pDst ){ return SUCCESS; } if( pDst == NULL ){ IDISPLAY_GetDeviceBitmap( po , &((WinDisplay*)po)->_dest ); }else{ IBITMAP_Release( ((WinDisplay*)po)->_dest ); ((WinDisplay*)po)->_dest = pDst; IBITMAP_AddRef( pDst ); } return SUCCESS; } IBitmap* WinDisplay::GetDestination(IDisplay *po){ IBITMAP_AddRef( ((WinDisplay*)po)->_dest ); return ((WinDisplay*)po)->_dest; } int WinDisplay::GetDeviceBitmap(IDisplay *po, IBitmap **ppIBitmap){ if( WinAppFrame::Get()->GetActiveAppllication()->GetDeviceBitmap() == (*ppIBitmap) ){ return SUCCESS; } (*ppIBitmap) = WinAppFrame::Get()->GetActiveAppllication()->GetDeviceBitmap(); IBITMAP_AddRef( (*ppIBitmap) ); return SUCCESS; }
AddRef() や Release() を忘れてはいけません。
IDISPLAY_CreateDIBitmap() については、内部的に WinBitmap を生成し、それを IDIB にキャストして返してやることにします。
int WinDisplay::CreateDIBitmap(IDisplay *po, IDIB **ppIDIB, uint8 colorDepth, uint16 w, uint16 h){ WinBitmap* pBitmap = new WinBitmap(); pBitmap->cx = w; pBitmap->cy = h; pBitmap->nDepth = colorDepth; // pitch を求める(4byteでアラインされている) pBitmap->nPitch = (((w * colorDepth + 7) >> 3) + 3) & ~3; if( h > 0 ){ pBitmap->pBmp = new byte[ pBitmap->nPitch * h ]; pBitmap->GetDIB()->pBmp = pBitmap->pBmp; } (*ppIDIB) = pBitmap; return SUCCESS; }
で、あとは IDISPLAY_Update() ですが、これは yaneGameSDK3rd を使って、ウインドウのセカンダリに転送するだけだと思うかもしれないですが、その場合、複数のアプレットをウインドウに表示して、それぞれの表示が重なっていて、Update() のタイミングが異なっていた場合、どちらが上になるか明確にならないため、表示がちらつくことになります。
これに対処するために、IDISPLAY_Update() では、デバイスビットマップと同じ大きさの画面(デバイスプライマリ)画面に反映し、ウインドウのアップデートフラグを立てておきます。
そして、全てのアプレットの処理が終わった後にプライマリに反映させることにします。
こうすれば全てのアプレットについて一定の重なりで画面を表示することが可能になります。
void WinDisplay::Update(IDisplay * po, boolean bDefer){ WinDisplay* display = (WinDisplay*)po; // デバイスビットマップ以外は反映させなくて良い if( (WinBitmap*)display->_dest != WinAppFrame::Get()->GetActiveAppllication()->GetDeviceBitmap() ){ return; } // デバイスビットマップをデバイスプライマリに描画 // 内部的には IBITMAP_BltIn() を使ってるだけ WinAppFrame::Get()->GetActiveAppllication()->UpdatePrimary(); // ウインドウのアップデートフラグを立てる WinAppFrame::Get()->UpdateDelayDisplay(); }
で、メインループでデバイスプライマリを描画すれば正常に描画出来ます。
これで IDisplay の実装は終わりです。次は……サウンド?