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