I have function, which creates Gdiplus::Bitmap.
Bitmap *LoadBitmapT(const unsigned char* fileBuffer, size_t length) {
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, length);
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, fileBuffer, length);
IStream* pstm;
CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
Gdiplus::Bitmap *bitmap = Gdiplus::Bitmap::FromStream(pstm, FALSE);
GlobalUnlock(m_hMem);
pstm->Release();
return bitmap;
}
As you can see, memory leak appears thanks to GlobalAlloc().
When I try to use GlobalFree(m_hmem) it solves my problem and memory leak disappear. But I am drawing obtained bitmap in another function with this code:
Graphics graphics(hdc);
graphics.DrawImage(bitmap, ....);
and when I don't use GlobalFree(), painted image is correct. But when I use mentioned function, than I lose correct image and it is image like blue screen.
Than I try to save pointer of m_hMem and call GlobalFree() after drawing bitmap. So, this is OK. But I need to use rotation on obtained bitmap, so when I call bitMap->RotateFlip(RotateNoneFlipX); than memory leaks appears again. Changing some color of pixel manually in bitmap makes same behaviour.
So, how can I release memory for this image and draw correctly this image. I need it, because I am drawing periodically a lot of images and so this allocates a lot of memory and then my program crash.
Edit
I tried this code:
Bitmap *LoadBitmapT(const unsigned char* fileBuffer, size_t length) {
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, length);
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, fileBuffer, length);
IStream* pstm;
CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
Gdiplus::Bitmap *bitmap = Gdiplus::Bitmap::FromStream(pstm, FALSE);
GlobalUnlock(m_hMem);
pstm->Release();
GlobalFree(m_hMem);
return NULL;
}
After this when I am looking on task manager, than I see that memory don't increase.
When I give bitmap->RotateFlip(RotateNoneFlipX);after Gdiplus::Bitmap *bitmap = Gdiplus::Bitmap::FromStream(pstm, FALSE); and code is same but only this one line is added, than memory is increasing.
I found answer.
This code deallocates everything correct.
HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, length);
BYTE* pmem = (BYTE*)GlobalLock(m_hMem);
memcpy(pmem, fileBuffer, length);
IStream* pstm;
CreateStreamOnHGlobal(m_hMem, FALSE, &pstm);
Gdiplus::Bitmap *bitmap = Gdiplus::Bitmap::FromStream(pstm, FALSE);
pstm->Release();
GlobalUnlock(m_hMem);
bitmap->RotateFlip(RotateNoneFlipX);
delete bitmap;
GlobalFree(m_hMem);
Need to call in correct order. So first delete bitmap and then GlobalFree
Related
I'm trying to use WIC to load an image into an in-memory buffer for further processing then write it back to a file when done. Specifically:
Load the image into an IWICBitmapFrameDecode.
The loaded IWICBitmapFrameDecode reports that its pixel format is GUID_WICPixelFormat24bppBGR. I want to work in 32bpp RGBA, so I call WICConvertBitmapSource.
Call CopyPixels on the converted frame to get a memory buffer.
Write the memory buffer back into an IWICBitmapFrameEncode using WritePixels.
This results in a recognizable image, but the resulting image is mostly blueish, as if the red channel is being interpreted as blue.
If I call WriteSource to write the converted frame directly, instead of writing the memory buffer, it works. If I call CopyPixels from the original unconverted frame (and update my stride and pixel formats accordingly), it works. It's only the combination of WICConvertBitmapSource plus the use of a memory buffer (CopyPixels + WritePixels) that causes the problem, but I can't figure out what I'm doing wrong.
Here's my code.
int main() {
IWICImagingFactory *pFactory;
IWICBitmapDecoder *pDecoder = NULL;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
(LPVOID*)&pFactory
);
// Load the image.
pFactory->CreateDecoderFromFilename(L"input.png", NULL, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &pDecoder);
IWICBitmapFrameDecode *pFrame = NULL;
pDecoder->GetFrame(0, &pFrame);
// pFrame->GetPixelFormat shows that the image is 24bpp BGR.
// Convert to 32bpp RGBA for easier processing.
IWICBitmapSource *pConvertedFrame = NULL;
WICConvertBitmapSource(GUID_WICPixelFormat32bppRGBA, pFrame, &pConvertedFrame);
// Copy the 32bpp RGBA image to a buffer for further processing.
UINT width, height;
pConvertedFrame->GetSize(&width, &height);
const unsigned bytesPerPixel = 4;
const unsigned stride = width * bytesPerPixel;
const unsigned bitmapSize = width * height * bytesPerPixel;
BYTE *buffer = new BYTE[bitmapSize];
pConvertedFrame->CopyPixels(nullptr, stride, bitmapSize, buffer);
// Insert image buffer processing here. (Not currently implemented.)
// Create an encoder to turn the buffer back into an image file.
IWICBitmapEncoder *pEncoder = NULL;
pFactory->CreateEncoder(GUID_ContainerFormatPng, nullptr, &pEncoder);
IStream *pStream = NULL;
SHCreateStreamOnFileEx(L"output.png", STGM_WRITE | STGM_CREATE, FILE_ATTRIBUTE_NORMAL, true, NULL, &pStream);
pEncoder->Initialize(pStream, WICBitmapEncoderNoCache);
IWICBitmapFrameEncode *pFrameEncode = NULL;
pEncoder->CreateNewFrame(&pFrameEncode, NULL);
pFrameEncode->Initialize(NULL);
WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat32bppRGBA;
pFrameEncode->SetPixelFormat(&pixelFormat);
pFrameEncode->SetSize(width, height);
pFrameEncode->WritePixels(height, stride, bitmapSize, buffer);
pFrameEncode->Commit();
pEncoder->Commit();
pStream->Commit(STGC_DEFAULT);
return 0;
}
The PNG encoder only supports GUID_WICPixelFormat32bppBGRA (BGR) for 32bpp as specified in PNG Native Codec official documentation. When you call it with GUID_WICPixelFormat32bppRGBA, it will not do channel switching. The pervert will just use your pixels as they were BGR, not RGB, and will not tell you there's a problem.
I don't know what you're trying to do, but in your example, you could just replace GUID_WICPixelFormat32bppRGBA by GUID_WICPixelFormat32bppBGRA in the call to WICConvertBitmapSource (and also replace the definition of the last pixelFormat variable to make sure your source code is correct, but it doesn't change anything).
PS: you can use Wic to save files, not need to create stream using another API, see my answer here: Capture screen using DirectX
I have some existing code that uses a CImage which has an alpha channel, and I need to rotate it.
I have found the following suggestion which converts the CImage to a GDI+ Bitmap and then rotates it, and the rotated result ends up back in the CImage.
Bitmap* gdiPlusBitmap=Bitmap::FromHBITMAP(atlBitmap.Detach());
gdiPlusBitmap->RotateFlip(Rotate90FlipNone);
HBITMAP hbmp;
gdiPlusBitmap->GetHBITMAP(Color::White, &hbmp);
atlBitmap.Attach(hbmp);
Apparently it works without actually copying the bitmap bytes, which is great, but the problem is that if you create a Bitmap object from a HBITMAP it throws away the alpha channel.
Apparently to preserve the alpha channel you must instead create the Bitmap using the constructor
Bitmap(
[in] INT width,
[in] INT height,
[in] INT stride,
[in] PixelFormat format,
[in] BYTE *scan0
);
So I'm trying to adapt the above to use this constructor, but the interaction between CImage and Bitmap is a bit confusing. I think I need to create the Bitmap like this
Bitmap* gdiPlusBitmap = new Bitmap(
pCImage->GetWidth(),
pCImage->GetHeight(),
pCImage->GetPitch(),
PixelFormat32bppARGB,
(BYTE *)pCImage->GetBits());
nGDIStatus = gdiPlusBitmap->RotateFlip(Rotate90FlipNone);
but I'm not sure how to make the CImage pick up the changes (so that I end up with the original CImage rotated), or where to delete the Bitmap object.
Does anyone know the correct way to do this, preserving the alpha channel ?
Ideally I'd like to avoid copying the bitmap data, but it's not mandatory.
You can use Gdiplus::Graphics to draw bitmap on CImage.
Note, hard coding PixelFormat32bppARGB can cause problems if image doesn't support alpha channel. I added some basic error check.
CImage image;
if (S_OK != image.Load(L"c:\\test\\test.png"))
{
AfxMessageBox(L"can't open");
return 0;
}
int bpp = image.GetBPP();
//get pixel format:
HBITMAP hbmp = image.Detach();
Gdiplus::Bitmap* bmpTemp = Gdiplus::Bitmap::FromHBITMAP(hbmp, 0);
Gdiplus::PixelFormat pixel_format = bmpTemp->GetPixelFormat();
if (bpp == 32)
pixel_format = PixelFormat32bppARGB;
image.Attach(hbmp);
//rotate:
Gdiplus::Bitmap bmp(image.GetWidth(), image.GetHeight(), image.GetPitch(), pixel_format, static_cast<BYTE*>(image.GetBits()));
bmp.RotateFlip(Gdiplus::Rotate90FlipNone);
//convert back to image:
image.Destroy();
if (image.Create(bmp.GetWidth(), bmp.GetHeight(), 32, CImage::createAlphaChannel))
{
Gdiplus::Bitmap dst(image.GetWidth(), image.GetHeight(), image.GetPitch(), PixelFormat32bppARGB, static_cast<BYTE*>(image.GetBits()));
Gdiplus::Graphics graphics(&dst);
graphics.DrawImage(&bmp, 0, 0);
}
I am trying to access the screen data from backbuffer using directx. However with the code below, the pointer to the pixel data has all 0.
I am really not sure if I am checking the data correctly. I am running this on Visual studio 2013, and using break points to check the pointer b. And its value is 0, all the time.
I am aware that I am not using IDirect3DDevice9::GetRenderTargetData method. This cause me to try to access the data in the device's memory I suppose. Would it prevent me from getting correct pixel data ?
d3dManager = new D3DManager(NULL, 600, 600);
IDirect3DDevice9 *device = d3dManager->getDevice();
IDirect3DSurface9 *ppBackBuffer = NULL;
HRESULT result = device->GetBackBuffer(
0,
0,
D3DBACKBUFFER_TYPE_MONO,
&ppBackBuffer
);
if (FAILED(result))
{
printf("vuhu");
return 1;
}
D3DSURFACE_DESC pDesc;
ppBackBuffer->GetDesc(&pDesc);
HANDLE *handle = NULL;
device->CreateOffscreenPlainSurface(pDesc.Width, pDesc.Height, pDesc.Format, D3DPOOL_SYSTEMMEM, &ppBackBuffer, handle);
D3DLOCKED_RECT lockedRectangle;
ppBackBuffer->LockRect(&lockedRectangle, NULL, D3DLOCK_DONOTWAIT);
void* bits = lockedRectangle.pBits;
int *a = (int*)(bits);
int *b = a + 120;
When you call CreateOffscreenPlainSurface here, and you pass in ppBackBuffer, it gets overwritten because it's creating new surface that's empty by default. In fact, you just leaked your reference to the backbuffer you obtained from GetBackBuffer.
D3DSURFACE_DESC pDesc;
ppBackBuffer->GetDesc(&pDesc);
HANDLE *handle = NULL;
device->CreateOffscreenPlainSurface(pDesc.Width, pDesc.Height, pDesc.Format, D3DPOOL_SYSTEMMEM, &ppBackBuffer, handle);
You need to actually call GetRenderTargetData to copy the data from the backbuffer (which is likely in video memory that is completely inaccessible to the CPU) to a surface you can read from the CPU.
BTW, check the HRESULTs of your calls, particularly CreateOffscreenPlainSurface and LockRect.
EDIT: If what you mean to do is create a new surface with the same properties as the backbuffer, then you should do:
D3DSURFACE_DESC pDesc;
ppBackBuffer->GetDesc(&pDesc);
ppBackBuffer->Release(); // <--- Let go of our reference to the back buffer surface
HANDLE *handle = NULL;
device->CreateOffscreenPlainSurface(pDesc.Width, pDesc.Height, pDesc.Format, D3DPOOL_SYSTEMMEM, &ppBackBuffer, handle);
There's nothing at all in your code that is actually moving the contents of the back buffer into the new surface. You are just reusing the variable IDirect3DSurface9 *ppBackBuffer.
I want to load an image (.bmp) file on a Win32 application, but I do not want to use the standard LoadBitmap/LoadImage from Windows API: I want it to load from a buffer that is already in memory. I can easily load a bitmap directly from a file and print it on the screen, but this issue is making me stuck.
What I'm looking for is a function that works like this:
HBITMAP LoadBitmapFromBuffer(char* buffer, int width, int height);
Try CreateBitmap():
HBITMAP LoadBitmapFromBuffer(char *buffer, int width, int height)
{
return CreateBitmap(width, height, 1, 24, buffer);
}
Nevermind, I found my solution! Here's the initializing code:
std::ifstream is;
is.open("Image.bmp", std::ios::binary);
is.seekg (0, std::ios::end);
length = is.tellg();
is.seekg (0, std::ios::beg);
pBuffer = new char [length];
is.read (pBuffer,length);
is.close();
tagBITMAPFILEHEADER bfh = *(tagBITMAPFILEHEADER*)pBuffer;
tagBITMAPINFOHEADER bih = *(tagBITMAPINFOHEADER*)(pBuffer+sizeof(tagBITMAPFILEHEADER));
RGBQUAD rgb = *(RGBQUAD*)(pBuffer+sizeof(tagBITMAPFILEHEADER)+sizeof(tagBITMAPINFOHEADER));
BITMAPINFO bi;
bi.bmiColors[0] = rgb;
bi.bmiHeader = bih;
char* pPixels = (pBuffer+bfh.bfOffBits);
char* ppvBits;
hBitmap = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (void**) &ppvBits, NULL, 0);
SetDIBits(NULL, hBitmap, 0, bih.biHeight, pPixels, &bi, DIB_RGB_COLORS);
GetObject(hBitmap, sizeof(BITMAP), &cBitmap);
CreateDIBSection can be a little complicated to use, but one of the things it can do is create a device-independent bitmap and give you a pointer to the buffer for the bitmap bits. Granted, you already have a buffer full of bitmap bits, but at least you could copy the data.
Speculating a bit: CreateDIBSection can also create bitmaps from file objects, and there's probably a way to get Windows to give you a file object representing a chunk of memory, which might trick CreateDIBSection into giving you a bitmap built directly from your buffer.
No, but you can create a new bitmap the size of the current one in memory, and write your memory structure onto it.
You're looking for the CreateBitmap function. Set lpvBits to your data.
I have a PNG-encoded icon as a byte array in memory. What is the recommended way of creating an HICON object from this byte array?
Imaginary bonus points if you know a solution without ATL or GDI+... :)
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, dataSize);
LPVOID pImage = GlobalLock(hMem);
memcpy(pImage, pngData, dataSize);
GlobalUnlock(hMem);
ATL::CComPtr<IStream> pStream;
CreateStreamOnHGlobal(hMem, TRUE, &pStream);
Gdiplus::Bitmap *pBitmap = new Gdiplus::Bitmap(pStream);
HICON YOUR_HICON = pBitmap->GetHICON();
It looks like you could do this with CreateBitmap and CreateIconIndirect, or maybe even just CreateIcon. Don't ask me for code because I'm not really familiar with this low-level graphics stuff.