Date: 2009jan12
Platform: Windows
Language: C++
Keywords: gdiplus, GDI+, Bitmap
Q. gdiplus: Load a GDI+ bitmap from memory?
A. The GDI+ Bitmap class can read from an IStream so if you make
your own class based on IStream it'll work. Here's such a class:
// An IStream based class for in-memory files
class ByteStream : public IStream
{
LONG m_refcount;
std::vector<BYTE> m_a;
long m_read_position;
public:
void Reserve(const size_t n)
{
m_a.reserve(n);
}
void Init()
{
m_a.clear();
m_read_position = 0;
}
ByteStream()
{
m_refcount = 1;
Init();
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void ** ppvObject)
{
if (iid == __uuidof(IUnknown)
|| iid == __uuidof(IStream)
|| iid == __uuidof(ISequentialStream))
{
*ppvObject = static_cast<IStream*>(this);
AddRef();
return S_OK;
}
else
{
return E_NOINTERFACE;
}
}
virtual ULONG STDMETHODCALLTYPE AddRef(void)
{
return (ULONG)InterlockedIncrement(&m_refcount);
}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
ULONG res = (ULONG) InterlockedDecrement(&m_refcount);
if (res == 0) delete this;
return res;
}
// ISequentialStream Interface
public:
virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead)
{
size_t bytes_left;
size_t bytes_out;
if (pcbRead != NULL) *pcbRead = 0;
if (m_read_position == m_a.size()) // EOF
{
return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
}
bytes_left = m_a.size() - m_read_position;
bytes_out = min(cb, bytes_left);
memcpy(pv, &m_a[m_read_position], bytes_out);
m_read_position += bytes_out;
if (pcbRead != NULL) *pcbRead = bytes_out;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE Write(void const* pv, ULONG cb, ULONG* pcbWritten)
{
size_t prev_size;
if (cb <= 0)
{
if (pcbWritten != NULL) *pcbWritten = 0;
return S_OK;
}
prev_size = m_a.size();
m_a.resize(prev_size + cb);
memcpy(&m_a[prev_size], pv, cb);
if (pcbWritten != NULL) *pcbWritten = cb;
return S_OK;
}
// IStream Interface
public:
virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*, ULARGE_INTEGER, ULARGE_INTEGER*, ULARGE_INTEGER*)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE Commit(DWORD)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE Revert(void)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE Clone(IStream **)
{
return E_NOTIMPL;
}
virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove, DWORD dwOrigin, ULARGE_INTEGER* lpNewFilePointer)
{
long start = 0;
long new_read_position;
switch(dwOrigin)
{
case STREAM_SEEK_SET:
start = 0;
break;
case STREAM_SEEK_CUR:
start = m_read_position;
break;
case STREAM_SEEK_END:
start = m_a.size();
break;
default:
return STG_E_INVALIDFUNCTION;
break;
}
new_read_position = start + (long)liDistanceToMove.QuadPart;
// Allowed to move to m_a.size() which is EOF
if (new_read_position < 0 || new_read_position > m_a.size())
{
return STG_E_SEEKERROR;
}
m_read_position = new_read_position;
if (lpNewFilePointer != NULL)
{
lpNewFilePointer->QuadPart = m_read_position;
}
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg, DWORD grfStatFlag)
{
if (pStatstg == NULL)
{
return STG_E_INVALIDFUNCTION;
}
ZeroMemory(pStatstg, sizeof(STATSTG));
pStatstg->cbSize.QuadPart = m_a.size();
return S_OK;
}
};
void ExampleUse()
{
ByteStream bs;
// Fill up the bs
// Turn it into a bitmap
Bitmap bitmap(&bs);
}