IImageからDIBを取得する(2)
自分の理解を深めるために、C++ で作ってみました。
で、作ってるときに気になった点は、
- DispFake を削除するときに、IDIB を Release() していないのがちょっと気持ち悪い。生存期間が、DispFake_BitBlt() で取得した IDIB より FakeDisp の方が長い場合は、DispFake は不正な IDIB を持っているということになる。まあ、このソースではあり得ないんだけど。
- DispFake_BitBlt() が2回以上実行された場合、同じポインタが渡された場合、pbmSource が NULL の場合を想定していない。
- FREE_VTBL の引数に IModule とか書いてる。中身は空だから別に良いんだけど……。
ぐらいかな。大した問題じゃないけどね(´・ω・`)
ちなみに、Backlight() とか SetAnnunciators() とかを委譲してるのは無駄な実装に思えるかもしれないけれど、IImage が内部でこのディスプレイを使って何を呼び出しているのか分からないので、これらも一応実装しておく必要があります。
以下ソース。長いので注意。あとコンパイル通しただけで実際には動かしてません。
#include <AEE.h> #include <AEEDisp.h> #include <AEEStdLib.h> #include "CaptureIImage.h" // IDisplay が定義されてないので、ここで定義する struct _IDisplay { AEEVTBL(IDisplay)* pvt; }; class DispFake : public IDisplay { public: DispFake(IShell* pIShell); ~DispFake(); IDisplay* GetDefaultDisplay() const; IDIB* GetDIB() const; private: static uint32 AddRef(IDisplay * po); static uint32 Release(IDisplay * po); 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: AEEVTBL( IDisplay ) _vtbl; uint32 _ref; IDIB* _pDIB; IDisplay* _pDisplay; }; // コンストラクタ DispFake::DispFake(IShell* pIShell) : _pDIB(NULL), _pDisplay(NULL) { pvt = NULL; // IDisplay の生成。失敗した場合は pvt が NULL のままになっている。 int ret = ISHELL_CreateInstance(pIShell, AEECLSID_DISPLAYCLONE, (void**)&_pDisplay); if (ret != SUCCESS) { return; } // 仮想テーブルの初期化 _vtbl.AddRef = &AddRef; _vtbl.Release = &Release; _vtbl.GetFontMetrics = &GetFontMetrics; _vtbl.MeasureTextEx = &MeasureTextEx; _vtbl.DrawText = &DrawText; _vtbl.DrawRect = &DrawRect; _vtbl.BitBlt = &BitBlt; _vtbl.Update = &Update; _vtbl.SetAnnunciators = &SetAnnunciators; _vtbl.Backlight = &Backlight; _vtbl.SetColor = &SetColor; _vtbl.GetSymbol = &GetSymbol; _vtbl.DrawFrame = &DrawFrame; _vtbl.CreateDIBitmap = &CreateDIBitmap; _vtbl.SetDestination = &SetDestination; _vtbl.GetDestination = &GetDestination; _vtbl.GetDeviceBitmap = &GetDeviceBitmap; _vtbl.SetFont = &SetFont; _vtbl.SetClipRect = &SetClipRect; _vtbl.GetClipRect = &GetClipRect; _vtbl.Clone = &Clone; _vtbl.MakeDefault = &MakeDefault; _vtbl.IsEnabled = &IsEnabled; _vtbl.NotifyEnable = &NotifyEnable; // 仮想テーブルをセット this->pvt = &_vtbl; } // デストラクタ DispFake::~DispFake() { // 解放 if (_pDisplay != NULL) { IDISPLAY_Release(_pDisplay); _pDisplay = NULL; } if (_pDIB != NULL) { IDIB_Release(_pDIB); _pDIB = NULL; } } // ポインタをそのまま返すだけで、AddRef はしない。 IDisplay* DispFake::GetDefaultDisplay() const { return _pDisplay; } IDIB* DispFake::GetDIB() const { return _pDIB; } // 以下は IDisplay インターフェースの実装 uint32 DispFake::AddRef(IDisplay * po) { return ++(static_cast<DispFake*>(po))->_ref; } uint32 DispFake::Release(IDisplay * po) { DispFake* pMe = static_cast<DispFake*>(po); if (--pMe->_ref == 0) { delete pMe; return 0; } return pMe->_ref; } int DispFake::GetFontMetrics(IDisplay * po,AEEFont nFont,int * pnAscent,int * pnDescent) { return IDISPLAY_GetFontMetrics(static_cast<DispFake*>(po)->_pDisplay, nFont, pnAscent, pnDescent); } int DispFake::MeasureTextEx(IDisplay * po, AEEFont nFont, const AECHAR * pcText,int nChars,int nMaxWidth, int * pnFits) { return IDISPLAY_MeasureTextEx(static_cast<DispFake*>(po)->_pDisplay, nFont, pcText, nChars, nMaxWidth, pnFits); } int DispFake::DrawText(IDisplay * po,AEEFont nFont, const AECHAR * pcText,int nChars,int x,int y,const AEERect * prcBackground,uint32 dwFlags) { return IDISPLAY_DrawText(static_cast<DispFake*>(po)->_pDisplay, nFont, pcText, nChars, x, y, prcBackground, dwFlags); } void DispFake::DrawRect(IDisplay * po,const AEERect * pRect,RGBVAL clrFrame, RGBVAL clrFill, uint32 dwFlags) { IDISPLAY_DrawRect(static_cast<DispFake*>(po)->_pDisplay, pRect, clrFrame, clrFill, dwFlags); } // このソースの肝; IImageがダミーディスプレイに渡すIDIBのポインタをコピーしてAddRefして「横取り」する void DispFake::BitBlt(IDisplay * po,int xDest,int yDest,int cxDest,int cyDest,const void * pbmSource,int xSrc,int ySrc,AEERasterOp dwRopCode){ DispFake* pMe = static_cast<DispFake*>(po); if (pbmSource == NULL || pMe->_pDIB == pbmSource) { return; } if (pMe->_pDIB != NULL) { IDIB_Release(pMe->_pDIB); } pMe->_pDIB = (IDIB*)pbmSource; IDIB_AddRef(pMe->_pDIB); } void DispFake::Update(IDisplay * po, boolean bDefer) { IDISPLAY_UpdateEx(static_cast<DispFake*>(po)->_pDisplay, bDefer); } void DispFake::SetAnnunciators(IDisplay * po, uint16 wVal, uint16 wMask) { IDISPLAY_SetAnnunciators(static_cast<DispFake*>(po)->_pDisplay, wVal, wMask); } void DispFake::Backlight(IDisplay * po,boolean bOn) { IDISPLAY_Backlight(static_cast<DispFake*>(po)->_pDisplay, bOn); } RGBVAL DispFake::SetColor(IDisplay * po, AEEClrItem clr, RGBVAL rgb) { return IDISPLAY_SetColor(static_cast<DispFake*>(po)->_pDisplay, clr, rgb); } AECHAR DispFake::GetSymbol(IDisplay * po,AEESymbol sym, AEEFont nFont) { return IDISPLAY_GetSymbol(static_cast<DispFake*>(po)->_pDisplay, sym, nFont); } // Helper routines - buttons, frames int DispFake::DrawFrame(IDisplay * po, AEERect * prc,AEEFrameType ft, RGBVAL rgbFill) { return IDISPLAY_DrawFrame(static_cast<DispFake*>(po)->_pDisplay, prc, ft, rgbFill); } int DispFake::CreateDIBitmap(IDisplay *po, IDIB **ppIDIB, uint8 colorDepth, uint16 w, uint16 h) { return IDISPLAY_CreateDIBitmap(static_cast<DispFake*>(po)->_pDisplay, ppIDIB, colorDepth, w, h); } int DispFake::SetDestination(IDisplay *po, IBitmap *pDst) { return IDISPLAY_SetDestination(static_cast<DispFake*>(po)->_pDisplay, pDst); } IBitmap* DispFake::GetDestination(IDisplay *po) { return IDISPLAY_GetDestination(static_cast<DispFake*>(po)->_pDisplay); } int DispFake::GetDeviceBitmap(IDisplay *po, IBitmap **ppIBitmap) { return IDISPLAY_GetDeviceBitmap(static_cast<DispFake*>(po)->_pDisplay, ppIBitmap); } IFont* DispFake::SetFont(IDisplay *po, AEEFont nFont, IFont *piFont) { return IDISPLAY_SetFont(static_cast<DispFake*>(po)->_pDisplay, nFont, piFont); } void DispFake::SetClipRect(IDisplay *po, const AEERect * pRect) { IDISPLAY_SetClipRect(static_cast<DispFake*>(po)->_pDisplay, pRect); } void DispFake::GetClipRect(IDisplay *po, AEERect * pRect) { IDISPLAY_GetClipRect(static_cast<DispFake*>(po)->_pDisplay, pRect); } int DispFake::Clone(IDisplay *po, IDisplay **ppIDisplayNew) { return IDISPLAY_Clone(static_cast<DispFake*>(po)->_pDisplay, ppIDisplayNew); } void DispFake::MakeDefault(IDisplay *po) { IDISPLAY_MakeDefault(static_cast<DispFake*>(po)->_pDisplay); } boolean DispFake::IsEnabled(IDisplay *po) { return IDISPLAY_IsEnabled(static_cast<DispFake*>(po)->_pDisplay); } int DispFake::NotifyEnable(IDisplay *po, AEECallback *pcb) { return IDISPLAY_NotifyEnable(static_cast<DispFake*>(po)->_pDisplay, pcb); } int CaptureIImage_GetDIB(IShell* pIShell, IImage* pImage, IDIB** ppDIB) { if (pIShell == NULL || pImage == NULL || ppDIB == NULL) { return EBADPARM; } // ダミーディスプレイを作成 DispFake* pFake = new DispFake(pIShell); if (pFake == NULL) { return ENOMEMORY; } if (pFake->pvt == NULL) { IDISPLAY_Release(pFake); return ENOMEMORY; } // ダミーディスプレイをIImageにセット IIMAGE_SetDisplay(pImage, pFake); // ダミーディスプレイに描画 = DIBの取得 IIMAGE_Draw(pImage, 0, 0); // デフォルトディスプレイをIImageにセット IIMAGE_SetDisplay(pImage, pFake->GetDefaultDisplay()); // 取得したDIBを返す if (pFake->GetDIB() == NULL) { IDISPLAY_Release(pFake); return EFAILED; } else { *ppDIB = pFake->GetDIB(); IDIB_AddRef(*ppDIB); IDISPLAY_Release(pFake); return SUCCESS; } }