備忘録 Windows

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

コメントを残す

メールアドレスが公開されることはありません。