GDI+のBitmapをWICから使う
WIC(Windows Imaging Component)は非常に良くできたライブラリなのですが、残念ながら、ドロー系の機能がさっぱりありません。線を引くぐらいならまだしも、文字を描画するとなると絶望的な状態です。なので、結局、一部の処理は、GDI+などを利用することになるのですが、GDI+ <-> WICのInteroperability Layer系の物はオフィシャルには公開されていないようです。大したことではないので、実装するのは難しくはないのですが、かといって、楽な作業かといえば、そんなに楽な物でもありません。それなりに時間のかかる代物です。
ということで、GDI+のBitmapをラップして、IWICBitmap相当の物を作ってみました。とはいっても、手抜きなので、BGR(24bit)以外は対応できていません。おパレット系の関数も動きません。
// このクラスは直接は利用しない(CWICGdiplusBgrBitmap::Lockが内部的に利用) class CWICGdiplusBitmapLock : public IWICBitmapLock { private: CWICGdiplusBitmapLock( IUnknown* parent, Gdiplus::Bitmap* bmp, const WICRect *prcLock, DWORD flags) { m_bmp = NULL; Gdiplus::Rect rc(prcLock->X, prcLock->Y, prcLock->Width, prcLock->Height); DWORD gdpFlags = 0; if(flags & WICBitmapLockRead) gdpFlags |= ImageLockModeRead; if(flags & WICBitmapLockWrite) gdpFlags |= ImageLockModeWrite; Gdiplus::Status status = bmp->LockBits(&rc, gdpFlags, PixelFormat24bppRGB, &m_bd); if(status != Ok) throw status; m_bmp = bmp; m_parent = parent; if(m_parent) m_parent->AddRef(); m_co = 1; // Lock behaves like QueryInterface } ~CWICGdiplusBitmapLock() { if(m_bmp) m_bmp->UnlockBits(&m_bd); if(m_parent) m_parent->Release(); } public: static CComPtr<IWICBitmapLock> Create( IUnknown* parent, Gdiplus::Bitmap* bmp, const WICRect *prcLock, DWORD flags) { return new CWICGdiplusBitmapLock(parent, bmp, prcLock, flags); } HRESULT __stdcall QueryInterface(REFIID iid, void **ppvObject) { *ppvObject = NULL; if(iid == __uuidof(IUnknown)) *ppvObject = dynamic_cast<IUnknown*>(this); else if(iid == __uuidof(IWICBitmapLock)) *ppvObject = dynamic_cast<IWICBitmapLock*>(this); if(*ppvObject) { AddRef(); return S_OK; } return E_NOINTERFACE; } ULONG __stdcall AddRef() { return ++m_co; } ULONG __stdcall Release() { ULONG n = --m_co; if(n == 0) delete this; return n; } HRESULT __stdcall GetDataPointer(UINT *pcbBufferSize, BYTE **ppbData) { *pcbBufferSize = m_bd.Stride * m_bd.Height; *ppbData = (BYTE*)m_bd.Scan0; return S_OK; } HRESULT __stdcall GetPixelFormat(WICPixelFormatGUID *pPixelFormat) { *pPixelFormat = GUID_WICPixelFormat24bppBGR; return S_OK; } HRESULT __stdcall GetSize(UINT *pWidth, UINT *pHeight) { *pWidth = (UINT)m_bd.Width; *pHeight = (UINT)m_bd.Height; return S_OK; } HRESULT __stdcall GetStride(UINT *pcbStride) { *pcbStride = m_bd.Stride; return S_OK; } private: IUnknown* m_parent; Gdiplus::Bitmap* m_bmp; Gdiplus::BitmapData m_bd; ULONG m_co; }; // GDI+のBitmapを使ったBGR(24bit)のIWICBitmapの実装 class CWICGdiplusBgrBitmap : public IWICBitmap { private: CWICGdiplusBgrBitmap(int width, int height) { m_bmp = new Gdiplus::Bitmap(width, height, PixelFormat24bppRGB); m_co = 0; } ~CWICGdiplusBgrBitmap() { delete m_bmp; } public: static CComPtr<CWICGdiplusBgrBitmap> Create(int width, int height) { return new CWICGdiplusBgrBitmap(width, height); } HRESULT __stdcall QueryInterface(REFIID iid, void **ppvObject) { *ppvObject = NULL; if(iid == __uuidof(IUnknown)) *ppvObject = dynamic_cast<IUnknown*>(this); else if(iid == __uuidof(IWICBitmapSource)) *ppvObject = dynamic_cast<IWICBitmapSource*>(this); else if(iid == __uuidof(IWICBitmap)) *ppvObject = dynamic_cast<IWICBitmap*>(this); if(*ppvObject) { AddRef(); return S_OK; } return E_NOINTERFACE; } ULONG __stdcall AddRef() { return ++m_co; } ULONG __stdcall Release() { ULONG n = --m_co; if(n == 0) delete this; return n; } HRESULT __stdcall CopyPalette(IWICPalette *pIPalette) { return E_FAIL; } HRESULT __stdcall CopyPixels( const WICRect *prc, UINT cbStride, UINT cbBufferSize, BYTE *pbBuffer) { Gdiplus::Rect rc(prc->X, prc->Y, prc->Width, prc->Height); Gdiplus::BitmapData bd; if(m_bmp->LockBits(&rc, ImageLockModeRead, PixelFormat24bppRGB, &bd) != Ok) return E_FAIL; int h = bd.Height; const BYTE* p = (const BYTE*)bd.Scan0; size_t bpl = bd.Width * 3; for(int y = 0; y < h; y++) { memcpy(pbBuffer, p, bpl); pbBuffer += cbStride; p += bd.Stride; } m_bmp->UnlockBits(&bd); return S_OK; } HRESULT __stdcall GetPixelFormat(WICPixelFormatGUID *pPixelFormat) { *pPixelFormat = GUID_WICPixelFormat24bppBGR; return S_OK; } HRESULT __stdcall GetResolution(double *pDpiX, double *pDpiY) { *pDpiX = m_bmp->GetHorizontalResolution(); *pDpiY = m_bmp->GetVerticalResolution(); return S_OK; } HRESULT __stdcall GetSize(UINT *puiWidth, UINT *puiHeight) { *puiWidth = (UINT)m_bmp->GetWidth(); *puiHeight = (UINT)m_bmp->GetHeight(); return S_OK; } HRESULT __stdcall Lock( const WICRect *prcLock, DWORD flags, IWICBitmapLock **ppILock) { try { *ppILock = CWICGdiplusBitmapLock::Create(this, m_bmp, prcLock, flags); return S_OK; } catch(...) { return E_FAIL; } } HRESULT __stdcall SetPalette(IWICPalette *pIPalette) { return E_FAIL; } HRESULT __stdcall SetResolution(double dpiX, double dpiY) { m_bmp->SetResolution((float)dpiX, (float)dpiY); return S_OK; } Gdiplus::Bitmap* GetBitmap() { return m_bmp; } private: Gdiplus::Bitmap* m_bmp; ULONG m_co; };
使い方は簡単で、要は、CWICGdiplusBgrBitmap::GetBitmapで内包するBitmapにアクセスできることです。WICを使ってのファイルの保存などについては、画像を保存などを参考にしてください。
// 1024x768のビットマップを作成 CComPtr<CWICGdiplusBgrBitmap> bmp = CWICGdiplusBgrBitmap::Create(1024, 768); // Graphicsオブジェクトを取得 Gdiplus::Graphics* g = Gdiplus::Graphics::FromBitmap(bmp->GetBitmap()); // Consolas 10ptで、50,50の位置にHello, worldと表示する g->DrawString( L"Hello, world", -1, &Gdiplus::Font(L"Consolas", 10), Gdiplus::PointF(50, 50), &SolidBrush(Color(0xff, 0, 0, 0))); // Graphicsオブジェクトを破棄 delete g; ...