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;
    }
}