• Advertisement
Sign in to follow this  

Dx9: HBITMAP to Texture

This topic is 4149 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I need to convert a HBITMAP hBitmap whit a image data to a D3D9Texture for bind onto two triangles ( quad ). How can face up this issue ? pseudocode Setup A --------- - buff=Malloc ( w*h*3 ) - GetDIBBits (..., buff, ) - Create EmptyTexture - Lock - MemCopy ( texture.pBits, buff ) - Unlock - Free ( buff ) - RenderTexture Setup B --------- - Create EmptyTexture - Lock - GetDIBBits (..., texture.pBits, ) - Unlock - RenderTexture The bitmap is previously get it from a window. ( Using HDC, CreateCompatibleBitmap, SelectObject... GDI functions ). May be i will find problems with image format ? texture pitch ? other ? I need a good performance, because i need implement this feature in every frame. All tips, suggestions are wellcome. Best regards. Thanx.

Share this post


Link to post
Share on other sites
Advertisement
Your method should be fine. I'd advise using a dynamic texture, to speed up the locking.
It's probably a good idea to use a DIB section rather than a bitmap (CreateDIBSection()), since you'll get better performance that way, and you have constant direct access to the bits.

As far as issues with format and pitch go, the format is D3DFMT_R8B8G8 I belive, which should be fine, and so long as you copy the bits out of the bitmap correctly (Using the texture pitch), that should be fine too.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Now, I implements setup B.

Code:

Texture Setup
--------------

if(FAILED(D3DXCreateTexture(pDevice,w,h,0,0,D3DFMT_R8G8B8,D3DPOOL_MANAGED,&q->pTexture))) return NULL;

// w & h aren't power of two

Render
------

i=0;
hDC=GetDC (hFWnd);
GetClientRect (hFWnd,&r);

HDC hMemDC = CreateCompatibleDC(hDC);
HBITMAP hBitmap=CreateCompatibleBitmap (hMemDC,r.right,r.bottom);

hgiobj=SelectObject (hMemDC,hBitmap);
b=BitBlt(hMemDC, 0, 0, r.right,r.bottom, hDC, 0, 0, SRCCOPY);
hgiobj=SelectObject (hMemDC,hgiobj);

hQuad->pTexture->GetLevelDesc (0,&desc);
hr=hQuad->pTexture->LockRect (0,&pLock,NULL,D3DLOCK_DISCARD);
bmpinfo=CreateBitmapInfoStruct (hFWnd,hBitmap);

ScanedLines=GetDIBits(hMemDC,hBitmap,0,bmpinfo->bmiHeader.biHeight,pLock.pBits,bmpinfo,DIB_RGB_COLORS);
hr=hQuad->pTexture->UnlockRect(0);

DeleteObject(hBitmap);
DeleteDC(hMemDC);
ReleaseDC(hFWnd, hDC);

hr=pDevice->SetTexture(0, hQuad->pTexture);

if (FAILED(pDevice->SetStreamSource(0,hQuad->pBuff,0,sizeof(CUSTOMVERTEX))))
return E_FAIL;

if (FAILED(pDevice->SetFVF(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1)))
return E_FAIL;

if (FAILED(pDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2)))
return E_FAIL;

i++;


All return codes looks good, included 'ScanedLines'. But the final result if a gray-white thin line, about 4 o 5 pixels, and the rest all-balck.

If i use:
hr=D3DXCreateTextureFromFile (pDevice,"C:\\alpha.tga",&hQuad->pTexture);
instead
hr=pDevice->SetTexture(0, hQuad->pTexture)

Tga looks fine. ( thats a test only ).

I normally works with Ogl, and this is my first app on DirectX, so i'am a noob, maybe, that's a obviously mistake on my code.

Best regards.

Share this post


Link to post
Share on other sites
Sorry, previously message was mine.

Quote:
Original post by Evil Steve
Your method should be fine. I'd advise using a dynamic texture, to speed up the locking.
It's probably a good idea to use a DIB section rather than a bitmap (CreateDIBSection()), since you'll get better performance that way, and you have constant direct access to the bits.

As far as issues with format and pitch go, the format is D3DFMT_R8B8G8 I belive, which should be fine, and so long as you copy the bits out of the bitmap correctly (Using the texture pitch), that should be fine too.


I take a glance over 'CreateDIBSection', i'm not sure that it's usefull for my app. Later, i will read function carefully. Thanks

Share this post


Link to post
Share on other sites
Firstly, I'd avoid creating the DC and bitmap each frame like the plague - it can't be very efficient. You should avoid creating and destroying resources completely in your main render loop.

Secondly, it seems that GetDIBits is failing (Or returning invalid values). The grey colour you described hints that it's debug filled data (MSVC debug fills memory allocated on the heap with 0xcd, which would show up as light grey if you made that into a RGB value).

I thought that GetDIBits only worked with a DIB section, not a bitmap. Does ScanedLines equal biHeight? What is biHeight? Have you tried using CreateDIBSection instead of CreateCompatibleBitmap? It should be faster anyway, since the GDI won't need to perform any colour conversion when you access the bits, and you don't need an internal memcpy() to copy the bits into your buffer.

As a sidenote - D3DXSaveSurfaceToFile is pretty handy when doing this sort of thing - it lets you see exactly what D3D has in your texture (Use GetSurfaceLevel() on the texture to get the top level surface to save).

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
Firstly, I'd avoid creating the DC and bitmap each frame like the plague - it can't be very efficient. You should avoid creating and destroying resources completely in your main render loop.

Secondly, it seems that GetDIBits is failing (Or returning invalid values). The grey colour you described hints that it's debug filled data (MSVC debug fills memory allocated on the heap with 0xcd, which would show up as light grey if you made that into a RGB value).

I thought that GetDIBits only worked with a DIB section, not a bitmap. Does ScanedLines equal biHeight? What is biHeight? Have you tried using CreateDIBSection instead of CreateCompatibleBitmap? It should be faster anyway, since the GDI won't need to perform any colour conversion when you access the bits, and you don't need an internal memcpy() to copy the bits into your buffer.

As a sidenote - D3DXSaveSurfaceToFile is pretty handy when doing this sort of thing - it lets you see exactly what D3D has in your texture (Use GetSurfaceLevel() on the texture to get the top level surface to save).



- What is biHeight? biHeight is the Heigh of hBitmap.
- Does ScanedLines equal biHeight? Yes.

- The texture pitch and width are not equal, maybe the prob, when copy image data.

For more faster, i use dynamic textures so i change:
if(FAILED(D3DXCreateTexture(pDevice,w,h,0,D3DUSAGE_DYNAMIC,D3DFMT_R8G8B8,D3DPOOL_DEFAULT,&q->pTexture)))
return E_FAIL;

But app doesn't work.

- I'm going to implement CreateDIBSection, u are right on ur comments.

Thanks Evil Steve. :D

Share this post


Link to post
Share on other sites
Quote:
Original post by CimentDegradat
- What is biHeight? biHeight is the Heigh of hBitmap.
I mean, what is it's value? If it's 0 or 1, that'll probably cause issues.

Quote:
Original post by CimentDegradat
- The texture pitch and width are not equal, maybe the prob, when copy image data.
Yes, the correct way to copy the data would be something like this (Untested):

D3DLOCKED_RECT theLock;
// This should be an array you create at init. It should be large enough
// to store the entire bitmap (width*height*3 bytes)
BYTE* pbyBits;

// Lock the texture, and copy the bitmap bits into the pbyBits array:
hr = hQuad->pTexture->LockRect(0, &theLock, NULL, D3DLOCK_DISCARD);
ScanedLines = GetDIBits(hMemDC, hBitmap, 0, bmpinfo->bmiHeader.biHeight, pbyBits, bmpinfo, DIB_RGB_COLORS);

// Copy the scanlines:
BYTE* pbyLock = (BYTE*)theLock.pBits;
for(int y=0; y<bmpinfo->bmiHeader.biHeight; ++y)
{
memcpy(pbyLock, pbyBits + bmpinfo->bmiHeader.biWidth*3*y, bmpinfo->bmiHeader.biWidth*3);
pbyLock += theLock.Pitch;
}

// Unlock the texture
hr = hQuad->pTexture->UnlockRect(0);


The point is that the pitch of the bitmap will probably be different from that of the texture, so you need to copy the data across one scanline at a time. That means that you can't pass the locked data pointer directly to GetDIBits(). It may be worthwhile checking if the pitch of the texture does equal the width times the bytes per pixel (3 in this case), and if it does then you can skip the double copy and read straight into the texture. Just a minor optimisation, however.
Also, I'm not sure offhand, but GetDIBits() might pad the data to a 4-byte boundary (Bitmap files have a restriction that each scanline must start on a 4-byte boundary). If you use CreateDIBSection(), this may change too.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Hi Evil,

I found a simplest way. ( tested )


GetClientRect (hFWnd,&r);
hDC=GetDC (hFWnd);

hr=hQuad->pTexture->GetSurfaceLevel (0,&pSurfaceDst);
hr=pSurfaceDst->GetDC(&hdcDest);

BitBlt(hdcDest, 0, 0, r.right,r.bottom, hDC, 0, 0, SRCCOPY);

hr=pSurfaceDst->ReleaseDC(hdcDest);
pSurfaceDst->Release();
pSurfaceDst=NULL;

ReleaseDC(hFWnd, hDC);
hr=pDevice->SetTexture(0, hQuad->pTexture);


But now i have a problem, if the window 'hFWnd' don't draw onto its client area (maybe it's minimized or a piece of window it's outside the screem desktop), i can't get it. arghhh, i try to force to paint with WM_PAINT message and InvalidateRect... but it doesn't work. Any idea ??? Or other method ?

Kind regards.

Share this post


Link to post
Share on other sites
Nope, it's impossible to do it your way if the window isn't a top level window that's in focus. The reason being that Windows doesn't keep a buffer for every windows client area, it only keeps a buffer for the portion that's visible. GDI calls will clip any drawing operations to the visible region of the client area.

The closest method to your way would be to double buffer the client area - Create a HBITMAP to represent the client area, and just BitBlt() it in your WM_PAINT handler. Then you'll be able to get access to your memory-resident HBITMAP whenever you want.
It's worth noting that although your method is easy, it's almost certainly not going to be as efficient as manually locking the texture. I'd guess that D3D will either create a HBITMAP behind the scenes, and copy onto the texture when you cann ReleaseDC(), or it'll use a DIB section to provide the GDI with access to the texture bits somehow.

Depending on how efficient this needs to be, it could be an idea to profile your results, or just start with the easiest method to implement, and see if that's efficient enough for your needs.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by Evil Steve
The closest method to your way would be to double buffer the client area - Create a HBITMAP to represent the client area, and just BitBlt() it in your WM_PAINT handler. Then you'll be able to get access to your memory-resident HBITMAP whenever you want.


The target window is the FlashPlayer, getting from FindWindow function, so a can t do this... any other idea ?

Thanks Evil Steve.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
Quote:
Original post by Evil Steve
The closest method to your way would be to double buffer the client area - Create a HBITMAP to represent the client area, and just BitBlt() it in your WM_PAINT handler. Then you'll be able to get access to your memory-resident HBITMAP whenever you want.


The target window is the FlashPlayer, getting from FindWindow function, so a can t do this... any other idea ?

Thanks Evil Steve.
Hmm, the only thing I can think of would be to use the Flash SDK (I presume it exists?) or embed a flash window in your own application somehow. I've never used flash embedded before so I don't know if/how this would work.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement