Creating a HBITMAP from a DirectX BackBuffer (One more quick question!)

Started by
45 comments, last by Endurion 18 years, 4 months ago
Creating a HBITMAP from a LPD3DXBUFFER. First of all is this possible? Below is my current code with the annotated space where this transformation is required. (If this post is in the wrong section please move it, i was not sure where to ask!)

HBITMAP MyClass::ScreenGrab()
{
//RenderTargetSurface.
IDirect3DSurface9* pRenderTargetSurface = NULL;

//DestinationTargetSurface
IDirect3DSurface9* pDestinationTargetSurface = NULL;

//DisplayMode
D3DDISPLAYMODE d3dDipMode;

//HBITMAP that will be the return of the method
HBITMAP hbm;

if (m_pd3dDevice == NULL)
return NULL;

//Get the client rectangle
RECT rc;
GetClientRect(myhWnd, &rc);
ClientToScreen(myhWnd, LPPOINT(&rc.left));
ClientToScreen(myhWnd, LPPOINT(&rc.right));


//GetRenderTargetSurface.
if(FAILED(m_pd3dDevice->GetRenderTarget(0, &pRenderTargetSurface)))
return NULL;

//Display Mode (d3dDipMode)
if(FAILED(m_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3dDipMode)))
return NULL;

//GetDestinationTargetSurface
if(FAILED(m_pd3dDevice->CreateOffscreenPlainSurface((rc.right - rc.left),
(rc.bottom - rc.top),
d3dDipMode.Format,
D3DPOOL_SYSTEMMEM,
&pDestinationTargetSurface,
NULL)))
return NULL;

//copy RenderTargetSurface -> DestTarget
if(FAILED(m_pd3dDevice->GetRenderTargetData(pRenderTargetSurface, pDestinationTargetSurface)))
return NULL;

LPD3DXBUFFER bufferedImage = NULL;

//Save the DestinationTargetSurface into memory (as a bitmap)
if(FAILED(D3DXSaveSurfaceToFileInMemory(&bufferedImage,
D3DXIFF_BMP,
pDestinationTargetSurface,
NULL,
NULL)))
{
return NULL;
}


//TRANSFORMATION REQUIRED HERE!!!!
//I WANT TO TRANSFORM bufferedImage INTO A HBITMAP SO THAT THE METHOD RETURNS
//THAT HBITMAP TO FULFIL MY CONTRACT.


//Release Resources
pRenderTargetSurface->Release();
pDestinationTargetSurface->Release();

//Return HBITMAT
return hbm;
}



Thanks for your help in advance,Alex. [Edited by - CHTHONIC on December 8, 2005 5:07:59 AM]
Advertisement
Made a few cockups with my original question, it is now editted and correct. Sorry about that!

Alex.
I got a reply on another forum as follows:

"Yes, just use CreateDIBitmap(). You'll need read the Windows Platform SDK documentition on bitmaps to learn how to calculate the various pointer values you need. After you've created the bitmap, you can release the ID3DXBuffer pointer."

Does this sound reasonable? I do not want to put the time and effort in to learn how to do this only to find it is not possible.

Thanks,

Alex.
Yes and no.

D3DXSaveSurfaceToFileInMemory creates a .bmp-File in memory in your case. This is not quite the same as the raw bitmap data which you'd need for CreateDIBitmap or CreateDIBSection.

You can however create a raw memory block the size of your texture. Then lock the texture and memcpy the image line per line (Pitch!) to this memory block. Make sure the memory block and the texture pixel data is the same byte format.

After that you can call CreateDIBitmap/CreateDIBSection with the pointer to your memory block.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Thanks Endurion.

Why line by line? Would something like the following suffice?
(where rc is the client rectangle)

int windowSize = (rc.bottom - rc.top) * (rc.right - rc.left) * 4;
LPBYTE lpSource = reinterpret_cast<LPBYTE>(lockedRC.pBits);
LPBYTE lpDestination = new BYTE[windowSize];
memcpy(lpDestination, lpSource, windowSize);

Thanks again for your help,

Alex.
Ok i attempted to do vaguely what you suggested but i am getting errors on myt memcpy method so obviously i am not doing that correctly. If you could point me in the right direction I would be much obliged. Here is the code as it stands:

HBITMAP MyClass::ScreenGrab(){   //RenderTargetSurface.   IDirect3DSurface9* pRenderTargetSurface = NULL;   //DestinationTargetSurface   IDirect3DSurface9* pDestinationTargetSurface = NULL;   //DisplayMode   D3DDISPLAYMODE d3dDipMode;   if (m_pd3dDevice == NULL)      return NULL;   RECT rc;   GetClientRect(myhWnd, &rc);   ClientToScreen(myhWnd, LPPOINT(&rc.left));   ClientToScreen(myhWnd, LPPOINT(&rc.right));   //GetRenderTargetSurface.   if(FAILED(m_pd3dDevice->GetRenderTarget(0, &pRenderTargetSurface)))	   return NULL;   //Display Mode (d3dDipMode)   if(FAILED(m_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3dDipMode)))       return NULL;      //GetDestinationTargetSurface   if(FAILED(m_pd3dDevice->CreateOffscreenPlainSurface((rc.right - rc.left),	                                               (rc.bottom - rc.top),                                                   d3dDipMode.Format,												   D3DPOOL_SYSTEMMEM,                                                   &pDestinationTargetSurface,												   NULL)))												   return NULL;   //copy RenderTargetSurface -> DestTarget   if(FAILED(m_pd3dDevice->GetRenderTargetData(pRenderTargetSurface, pDestinationTargetSurface)))	   return NULL;   //Create a lock on the DestinationTargetSurface   D3DLOCKED_RECT lockedRC;   pDestinationTargetSurface->LockRect(&lockedRC,	                                   &rc,									   D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY);   int windowSize = (rc.bottom - rc.top) * (rc.right - rc.left) * 4;   LPBYTE lpSource = reinterpret_cast<LPBYTE>(lockedRC.pBits);   LPBYTE lpDestination = new BYTE[windowSize];   memcpy(lpDestination, lpSource, windowSize);    // ERRORS ON MEMCPY.. NO MEANINGFUL ERROR (then againt his is C++.. oh how   // i HATE C++... bloody job... I'm a C# developer for gods sake!)    //Create a handle to a bitmap (return from CreateDIBitmap())   HBITMAP hbm;   //Create a Device Context (parameter 1 for CreateDIBitmap())   HDC hdcBitmap = NULL;   pDestinationTargetSurface->GetDC(&hdcBitmap);   //Create a BITMAPINFO/BITMAPINFOHEADER structure and fill it(parameter 2 & 5 for CreateDIBitmap())   BITMAPINFO	bmpInfo;   ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));   bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);   bmpInfo.bmiHeader.biBitCount=32;   bmpInfo.bmiHeader.biCompression = BI_RGB;   bmpInfo.bmiHeader.biWidth=GetSystemMetrics(SM_CXSCREEN);   bmpInfo.bmiHeader.biHeight=GetSystemMetrics(SM_CYSCREEN);   bmpInfo.bmiHeader.biSizeImage=abs(bmpInfo.bmiHeader.biHeight)*bmpInfo.bmiHeader.biWidth*bmpInfo.bmiHeader.biBitCount/8;   hbm = CreateDIBitmap(hdcBitmap,					    &bmpInfo.bmiHeader,						CBM_INIT,						lpDestination,						&bmpInfo,						DIB_RGB_COLORS);      //Release Resources   pDestinationTargetSurface->ReleaseDC(hdcBitmap);   pRenderTargetSurface->Release();   pDestinationTargetSurface->Release();   return hbm;}




Thanks again,

Alex

[Edited by - Muhammad Haggag on December 1, 2005 7:44:47 AM]
Source tags, please. Read the rules and the FAQs.

Line by line because the driver might add some data between the lines of your texture data (for performance reason). Never ever assume that the pixel data is in one continious block:

int windowSize = (rc.bottom - rc.top) * (rc.right - rc.left) * 4;LPBYTE lpSource = reinterpret_cast<LPBYTE>(lockedRC.pBits);LPBYTE lpDestination = new BYTE[windowSize];for ( int iY = 0; iY < rc.right - rc.left; ++iY ){  memcpy( lpDestination, lpSource, 4 * ( rc.bottom - rc.top ) );  lpSource += lockedRC.Pitch;}


Also, this will only work if you really create a 32 bit DIB, 24 bit will not be enough.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

Thanks again Endurion,

I tried what you suggested but it is still churning out the same non-descript error on the memcpy (suggesting there is a Null Pointer in the memcpy assembler code).

Alex.
Did you check if the LockRect works?

AFAIK LockRect doesn't work on the front buffer if the lockable flag is not provided.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

This topic is closed to new replies.

Advertisement