Gdiplus::Bitmap::FromStreamでjpeg画像が壊れた
メモリ内にあるjpeg画像をGdiplus::Bitmap::FromStreamを使用してDirectXのテクスチャとして使用できるようにしたところ、画像が壊れてしまった。(下8割ほどがグレーになった)
原因はストリームに渡すためにGlobalAllocで確保したメモリを、Gdiplus::Bitmapが使い終わるより先に解放したからだった。
FromStreamだけでなく、そのあとのGetPixelやLockBitsを呼び出すときにも、GlobalAllocで確保したメモリは解放してはいけないらしい。
(pngではFromStreamのときに解放されていなければ大丈夫だった)
以下、メモリ内のjpegをピクセルデータにするコード。
void CreateJpegTexture(const void* iData, uint32_t iDataSize)
{
// グローバルメモリを確保してロック
HGLOBAL aGlobalHandle = GlobalAlloc(GMEM_MOVEABLE, iDataSize);
if(aGlobalHandle)
{
void* aGlobalBuffer = GlobalLock(aGlobalHandle);
if(aGlobalBuffer)
{
// グローバルメモリにコピー
CopyMemory(aGlobalBuffer, iData, iDataSize);
// ストリーム作成
IStream* aIStream = NULL;
if(CreateStreamOnHGlobal(aGlobalHandle, FALSE, &aIStream) == S_OK)
{
// GDI+ビットマップ生成(グローバルメモリは解放)
Gdiplus::Bitmap* aBitmap = Gdiplus::Bitmap::FromStream(aIStream);
aIStream->Release();
if(aBitmap && aBitmap->GetLastStatus() == Gdiplus::Ok)
{
// バッファにコピー
UINT aWidth = aBitmap->GetWidth();
UINT aHeight = aBitmap->GetHeight();
Gdiplus::Rect aRect(0, 0, aWidth, aHeight);
Gdiplus::BitmapData aBitmapData;
aBitmap->LockBits(&aRect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &aBitmapData);
const uint8_t* aPixels = reinterpret_cast<const uint8_t*>(aBitmapData.Scan0);
std::vector<uint32_t> aBuffer(aWidth * aHeight);
for(UINT aY = 0; aY != aHeight; ++aY)
{
for(UINT aX = 0; aX != aWidth; ++aX)
{
const uint8_t* aPixel = aPixels + aY * aBitmapData.Stride + aX * 4;
uint32_t aR = aPixel[2];
uint32_t aG = aPixel[1];
uint32_t aB = aPixel[0];
uint32_t aA = aPixel[3];
aR = aR * aA / 255;
aG = aG * aA / 255;
aB = aB * aA / 255;
aBuffer[aY * aWidth + aX] = (aA << 24) | (aB << 16) | (aG << 8) | aR;
}
}
aBitmap->UnlockBits(&aBitmapData);
// 解放
delete aBitmap;
// ~aBufferにピクセルデータが格納されている~
}
}
// アンロック
GlobalUnlock(aGlobalHandle);
}
// 解放
GlobalFree(aGlobalHandle);
}
}
