I'm attempting to overlay a bitmap on some video. I create a bitmap in memory, then I call SelectObject to select it into a memoryDC, so I can perform a DrawText operation on it.
I'm not getting any results on screen at all - can anyone suggest why?
thanks
HRESULT MyGraph::MixTextOverVMR9(LPCTSTR szText)
{
// create a bitmap object using GDI, render the text to it accordingly
// then Sets the bitmap as an alpha bitmap to the VMR9, so that it can be overlayed.
HRESULT hr = S_OK;
CBitmap bmpMem;
CFont font;
LOGFONT logicfont;
CRect rcText;
CRect rcVideo;
VMR9AlphaBitmap alphaBmp;
HWND hWnd = this->GetFirstRendererWindow();
COLORREF clrText = RGB(255, 255, 0);
COLORREF clrBlack = RGB(0,0,0);
HDC hdcHwnd = NULL;
CDC dcMem;
LONG lWidth;
LONG lHeight;
if( ! m_spVideoRenderer.p )
return E_NOINTERFACE;
if( !m_spWindowlessCtrl.p )
return E_NOINTERFACE;
if( ! m_spIMixerBmp9.p )
{
m_spIMixerBmp9 = m_spVideoRenderer;
if( ! m_spIMixerBmp9.p )
return E_NOINTERFACE;
}
// create the font..
LPCTSTR sFont = _T("Times New Roman");
memset(&logicfont, 0, sizeof(LOGFONT));
logicfont.lfHeight = 42;
logicfont.lfWidth = 20;
logicfont.lfStrikeOut = 0;
logicfont.lfUnderline = 0;
logicfont.lfItalic = FALSE;
logicfont.lfWeight = FW_NORMAL;
logicfont.lfEscapement = 0;
logicfont.lfCharSet = ANSI_CHARSET;
logicfont.lfQuality = ANTIALIASED_QUALITY;
logicfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
wcscpy_s( &logicfont.lfFaceName[0], wcslen(sFont)*2, sFont );
font.CreateFontIndirectW(&logicfont);
// create a compatible memDC from the video window's HDC
if( hWnd == NULL )
return E_FAIL;
hdcHwnd = GetDC(hWnd);
dcMem = CreateCompatibleDC(hdcHwnd);
// get the required bitmap metrics from the MediaBuffer
if( ! SUCCEEDED(m_spWindowlessCtrl->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL)) )
return E_FAIL;
// create a bitmap for the text
bmpMem.CreateCompatibleBitmap(dcMem.m_hDC, lWidth, lHeight);
SelectBitmap (dcMem.m_hDC, bmpMem);
SetBkMode (dcMem.m_hDC, TRANSPARENT);
SetTextColor (dcMem.m_hDC, clrText);
SelectFont (dcMem.m_hDC, font.m_hFont);
// draw the text
DrawTextW(dcMem.m_hDC, szText, wcslen(szText), rcText, DT_CALCRECT | DT_NOPREFIX );
DrawTextW(dcMem.m_hDC, szText, wcslen(szText), rcText, DT_NOPREFIX );
// Set the alpha bitmap on the VMR9 renderer
memset(&alphaBmp, 0, sizeof(VMR9AlphaBitmap));
alphaBmp.rDest.left = 0;
alphaBmp.rDest.top = 0.5;
alphaBmp.rDest.right = 0.5;
alphaBmp.rDest.bottom = 1;
alphaBmp.dwFlags = VMR9AlphaBitmap_hDC;
alphaBmp.hdc = dcMem.m_hDC;
alphaBmp.pDDS = NULL;
alphaBmp.rSrc = rcText; // rect to copy from the source image
alphaBmp.fAlpha = 0.5f; // transparency value (1.0 is opaque, 0.0 is transparent)
alphaBmp.clrSrcKey = clrText;
// alphaBmp.dwFilterMode = MixerPref9_AnisotropicFiltering;
hr = m_spIMixerBmp9->SetAlphaBitmap(&alphaBmp);
DeleteDC(hdcHwnd);
dcMem.DeleteDC();
bmpMem.DeleteObject();
font.DeleteObject();
return hr;
}
I got this one solved, so if anyone else needs to know how it's done...
HRESULT MyGraph::MixTextOverVMR9(LPCTSTR szText)
{
// create a bitmap object using GDI, render the text to it accordingly
// then Sets the bitmap as an alpha bitmap to the VMR9, so that it can be overlayed.
HRESULT hr = S_OK;
CBitmap bmpMem;
CFont font;
LOGFONT logicfont;
CRect rcText;
CRect rcVideo;
VMR9AlphaBitmap alphaBmp;
HWND hWnd = this->GetFirstRendererWindow();
COLORREF clrText = RGB(255, 255, 0);
COLORREF clrBlack = RGB(0,0,0);
HDC hdcHwnd = NULL;
CDC dcMem;
LONG lWidth;
LONG lHeight;
if( ! m_spVideoRenderer.p )
return E_NOINTERFACE;
if( !m_spWindowlessCtrl.p )
return E_NOINTERFACE;
if( ! m_spIMixerBmp9.p )
{
m_spIMixerBmp9 = m_spVideoRenderer;
if( ! m_spIMixerBmp9.p )
return E_NOINTERFACE;
}
// create the font..
LPCTSTR sFont = _T("Impact");
memset(&logicfont, 0, sizeof(LOGFONT));
logicfont.lfHeight = 24;
//logicfont.lfWidth = 24;
logicfont.lfStrikeOut = 0;
logicfont.lfUnderline = 0;
logicfont.lfItalic = FALSE;
logicfont.lfWeight = FW_NORMAL;
logicfont.lfEscapement = 0;
logicfont.lfCharSet = ANSI_CHARSET;
logicfont.lfQuality = ANTIALIASED_QUALITY;
logicfont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
wcscpy_s( &logicfont.lfFaceName[0], wcslen(sFont)*2, sFont );
font.CreateFontIndirectW(&logicfont);
// create a compatible memDC from the video window's HDC
if( hWnd == NULL )
return E_FAIL;
hdcHwnd = GetDC(hWnd);
dcMem = CreateCompatibleDC(hdcHwnd);
// get the required bitmap metrics from the MediaBuffer
if( ! SUCCEEDED(m_spWindowlessCtrl->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL)) )
return E_FAIL;
// create a bitmap for the text
bmpMem.CreateCompatibleBitmap(dcMem.m_hDC, lWidth, lHeight);
SelectBitmap (dcMem.m_hDC, bmpMem);
SetBkColor (dcMem.m_hDC, clrBlack);
SetBkMode (dcMem.m_hDC, TRANSPARENT);
SetTextColor (dcMem.m_hDC, clrText);
SelectFont (dcMem.m_hDC, font.m_hFont);
// draw the text
DrawTextW(dcMem.m_hDC, szText, wcslen(szText), rcText, DT_CALCRECT | DT_NOPREFIX );
DrawTextW(dcMem.m_hDC, szText, wcslen(szText), rcText, DT_NOPREFIX );
// Set the alpha bitmap on the VMR9 renderer
memset(&alphaBmp, 0, sizeof(VMR9AlphaBitmap));
// top left
alphaBmp.rDest.left = 0.0f + EDGE_BUFFER;
alphaBmp.rDest.top = 0.0f + EDGE_BUFFER;
alphaBmp.rDest.right = ((float)rcText.right / (float) lWidth) + EDGE_BUFFER;
alphaBmp.rDest.bottom = ((float)rcText.bottom / (float)lHeight) + EDGE_BUFFER;
// top right
alphaBmp.rDest.left = 1.0f - ( (float)rcText.right / (float)lWidth)- EDGE_BUFFER;
alphaBmp.rDest.top = 0.0f + EDGE_BUFFER;
alphaBmp.rDest.right = 1.0f - EDGE_BUFFER;
alphaBmp.rDest.bottom = ((float)rcText.bottom / (float)lHeight) + EDGE_BUFFER;
/*
// bottom left
alphaBmp.rDest.left = 0.0f + EDGE_BUFFER;
alphaBmp.rDest.top = 1.0f - ((float)rcText.bottom / (float)lHeight) - EDGE_BUFFER;
alphaBmp.rDest.right = ((float)rcText.right / (float) lWidth) + EDGE_BUFFER;
alphaBmp.rDest.bottom = 1.0f - EDGE_BUFFER;
*/
/*
// bottom right
alphaBmp.rDest.left = 1.0f - ( (float)rcText.right / (float)lWidth)- EDGE_BUFFER;
alphaBmp.rDest.top = (float)(lHeight - rcText.bottom) / (float)lHeight - EDGE_BUFFER;
alphaBmp.rDest.right = 1.0f - EDGE_BUFFER;
alphaBmp.rDest.bottom = 1.0f - EDGE_BUFFER;
*/
alphaBmp.dwFlags = VMR9AlphaBitmap_hDC | VMRBITMAP_SRCCOLORKEY;
alphaBmp.hdc = dcMem.m_hDC;
alphaBmp.pDDS = NULL;
alphaBmp.rSrc = rcText; // rect to copy from the source image
alphaBmp.fAlpha = 0.3f; // transparency value (1.0 is opaque, 0.0 is transparent)
alphaBmp.clrSrcKey = clrBlack;
alphaBmp.dwFilterMode = MixerPref9_PointFiltering;
hr = m_spIMixerBmp9->SetAlphaBitmap(&alphaBmp);
DeleteDC(hdcHwnd);
dcMem.DeleteDC();
bmpMem.DeleteObject();
font.DeleteObject();
return hr;
}
Related
I want to display a images on window, am giving image data as in buffer &CaptureBuffer to CreateDIBSection, CreateDIBForVideo method is calling from thread on main().
dont know where im going wrong it is showing black window.
void CreateDIBForVideo()
{
// ScreenCaptureProcessorGDI is a class it have initialization for capture window screen
screenObject = new ScreenCaptureProcessorGDI();
screenObject->init();
HDC DisplayDC = CreateDC((LPCWSTR)"DISPLAY", NULL, NULL, NULL);
BITMAPINFO bmpInfo = { 0 };
bmpInfo.bmiHeader.biSize= sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = screenObject->lOutputDuplDesc.ModeDesc.Width;
bmpInfo.bmiHeader.biHeight= screenObject->lOutputDuplDesc.ModeDesc.Height;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 32;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biSizeImage = (4 * screenObject->lOutputDuplDesc.ModeDesc.Width * screenObject->lOutputDuplDesc.ModeDesc.Height);
bmpInfo.bmiHeader.biXPelsPerMeter = 0;
bmpInfo.bmiHeader.biYPelsPerMeter = 0;
bmpInfo.bmiHeader.biClrUsed = 0;
bmpInfo.bmiHeader.biClrImportant = 0;
CaptureBuffer = NULL;
HDC pXorDC = CreateCompatibleDC(DisplayDC);
HBITMAP hXorDib = CreateDIBSection(DisplayDC, &bmpInfo, DIB_RGB_COLORS, (void**)&CaptureBuffer, NULL, 0);
hXorTemp = (HBITMAP)SelectObject(pXorDC, hXorDib);
// startGrab this thread capture a windows screen after init()
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&startGrab, NULL, 0, NULL);
}
void startGrab()
{
for (int index = 0; index < 100; index++)
{
// grabImage will capture window screen and send image as a buffer to `CaptureBuffer`
screenObject->grabImage();
PaintViewerWindow();
UpdateWindow(global_hWnd);
::Sleep(2000);
}
}
void PaintViewerWindow()
{
HDC paintDC;
PAINTSTRUCT ps;
paintDC = BeginPaint(global_hWnd, &ps);
SetStretchBltMode(paintDC, HALFTONE);
BitBlt(paintDC, 0, 0, 1366, 768, pXorDC, 0, 0, SRCCOPY);
EndPaint(global_hWnd, &ps);
}
I find a solution for the above problem
It because of UCHAR *CaptureBuffer am directly passing CaptureBuffer to the CreateDIBSection now i changed to copying UCHAR *CaptureBuffer to char *DisplayBuffer its working fine now.
UCHAR *CaptureBuffer = NULL;
char *DisplayBuffer = NULL;
long CaptureSize = NULL;
void saveImage(unsigned int frame_num, BITMAPINFO &lBmpInfo, std::unique_ptr<BYTE> pBuf, UCHAR* &CaptureBuffer, long &CaptureSize)
{
BITMAPFILEHEADER bmpFileHeader;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + lBmpInfo.bmiHeader.biSizeImage;
bmpFileHeader.bfType = 'MB';
bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
CaptureSize = lBmpInfo.bmiHeader.biSizeImage;
CaptureBuffer = (UCHAR*)malloc(CaptureSize);
memcpy(CaptureBuffer, pBuf.get(), lBmpInfo.bmiHeader.biSizeImage);
//Here am copying CaptureBuffer(uchar) to DisplayBuffer(char)
memcpy(DisplayBuffer, CaptureBuffer, CaptureSize);
lresult = 0;
}
void CreateDIBForVideo()
{
// ScreenCaptureProcessorGDI is a class it have initialization for capture window screen
screenObject = new ScreenCaptureProcessorGDI();
screenObject->init();
HDC DisplayDC = CreateDC((LPCWSTR)"DISPLAY", NULL, NULL, NULL);
BITMAPINFO bmpInfo = { 0 };
bmpInfo.bmiHeader.biSize= sizeof(BITMAPINFOHEADER);
bmpInfo.bmiHeader.biWidth = screenObject->lOutputDuplDesc.ModeDesc.Width;
bmpInfo.bmiHeader.biHeight= screenObject->lOutputDuplDesc.ModeDesc.Height;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 32;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biSizeImage = (4 * screenObject->lOutputDuplDesc.ModeDesc.Width * screenObject->lOutputDuplDesc.ModeDesc.Height);
bmpInfo.bmiHeader.biXPelsPerMeter = 0;
bmpInfo.bmiHeader.biYPelsPerMeter = 0;
bmpInfo.bmiHeader.biClrUsed = 0;
bmpInfo.bmiHeader.biClrImportant = 0;
CaptureBuffer = NULL;
HDC pXorDC = CreateCompatibleDC(DisplayDC);
HBITMAP hXorDib = CreateDIBSection(DisplayDC, &bmpInfo, DIB_RGB_COLORS, (void**)&DisplayBuffer, NULL, 0);
hXorTemp = (HBITMAP)SelectObject(pXorDC, hXorDib);
// startGrab this thread capture a windows screen after init()
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&startGrab, NULL, 0, NULL);
}
I'm trying to read all pixels on a given area of a HDC to find if a color is present, currently I came up with:
IDirect3DSurface9* pSurface = 0;
p1->CreateOffscreenPlainSurface(1280, 1024,D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pSurface, NULL);
p1->GetFrontBufferData(0, pSurface);
//assert( pSurface );
if( pSurface && GetTickCount() > dwGAKS_Fix )
{
HDC dc;
pSurface->GetDC( &dc );
COLORREF dpurp = D3DCOLOR_ARGB (255,102,0 ,153);
for( DWORD h = 610; h <= 670; h++ )
{
for( DWORD w = 480; w<=530; w++ )
{
COLORREF dwPixel = GetPixel( dc, h, w );
// CString strPixel; strPixel.Format( "Pixel col: %u at: %u X %u", dwPixel, d, i );
//if( dx_Font )
if( dwPixel == dpurp )
{
dx_Font->DrawTextA(NULL, "Shoot", strlen("Shoot"), &pos, DT_NOCLIP, D3DCOLOR_XRGB(0, 255, 0));
}
else
dx_Font->DrawTextA(NULL, "NoShoot", strlen("NoShoot"), &pos, DT_NOCLIP, D3DCOLOR_XRGB(0, 255, 0));
}
}
dwGAKS_Fix = GetTickCount() + 15;
pSurface->ReleaseDC( dc );
pSurface->Release();
But this solution is slow, very slow, I need something somewhat more..uh professional
edit
D3DLOCKED_RECT d3dlocked;
if( D3D_OK == pSurface->LockRect( &d3dlocked, 0, 0 ) )
{
UINT *pixels=(UINT *)locked.pBits;
if(pixels[52+15*1024]&0xFFFFFF00==dpurp)
{
}
pSurface->UnlockRect();
}
GetPixel is always slow. You can get direct access to the bits in the off-screen surface using IDirect3DSurface9::LockRect and then scan through the bitmap yourself, which should be much quicker.
(Edit) Any given pixel (x,y) is the 32 bit value found at:
*(DWORD*)(((BYTE*)d3dlocked.pBits) + y * d3dlocked.Pitch + x * sizeof(DWORD));
You should AND the value with 0x00ffffff to ignore the alpha channel.
I created a layered window (with WS_EX_LAYERED), size of about 400X300 px.
When drawing the window (using UpdateLayeredWindow) everything works great.
The problem is that I'm unable to get the HBITMAP of the window after drawing it.
When trying to get the HBITMAP through the window's HDC, I get an empty (black) bitmap, the size of my entire desktop (1920X1080 px insted of 400X300 px).
Does anybody know if it is even possible to get the HDC\HBITMAP of a layered window?
Code samples
Here's the code of how I draw the layered window (again, works great):
void MyLayeredWindow::DrawLayered() const
{
RECT rcWindow;
GetWindowRect(rcWindow);
int nWidth = abs(rcWindow.right - rcWindow.left);
int nHeight = abs(rcWindow.bottom - rcWindow.top);
// Create 32Bit bitmap to apply transparency
// (have to set negative height because if not the (0,0) point would be the bottom left instead of top left)
VOID *ppvBits = NULL;
BITMAPINFO BitmapInfo = {0};
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = nWidth;
BitmapInfo.bmiHeader.biHeight = -nHeight;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
// Copy view buffer to a temp DC and bitmap
HDC hDcTemp = ::CreateCompatibleDC(NULL);
assert(hDcTemp);
HBITMAP hBitmapTemp = ::CreateDIBSection(hDcTemp, &BitmapInfo, DIB_RGB_COLORS, &ppvBits, NULL, 0);
assert(hBitmapTemp && hBitmapTemp!=(HBITMAP)ERROR_INVALID_PARAMETER)
::SelectObject(hDcTemp, hBitmapTemp);
// Darwing the window's conent here
// ....
// ....
// Call UpdateLayeredWindow
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 190;
blend.AlphaFormat = AC_SRC_ALPHA;
SIZE sizeWnd = {0};
sizeWnd.cx = nWidth;
sizeWnd.cy = nHeight;
POINT ptPos = {0};
ptPos.x = rcWindow.left;
ptPos.y = rcWindow.top;
POINT ptSrc = {0,0};
::UpdateLayeredWindow(m_hWnd, NULL, &ptPos, &sizeWnd, hDcTemp, &ptSrc, 0, &blend, ULW_ALPHA);
if(hDcTemp)
::DeleteDC(hDcTemp);
if(hBitmapTemp)
::DeleteObject(hBitmapTemp);
}
Here's the code of how I capture the window's bitmap and save it to a file:
(NOTICE: It works of "normal" windows, such as the Calculator)
bool MyLayeredWindow::SaveBitmapFile(__in const HWND& hWnd, __in const wstring& sFileName)
{
HDC hDC = ::GetDC(hWnd);
// get bitmap of DC
HBITMAP hBmp = (HBITMAP)::GetCurrentObject( hDC, OBJ_BITMAP );
// get info of bitmap
BITMAPINFO stBmpInfo;
stBmpInfo.bmiHeader.biSize = sizeof( stBmpInfo.bmiHeader );
stBmpInfo.bmiHeader.biBitCount = 0;
GetDIBits( hDC, hBmp, 0, 0, NULL, &stBmpInfo, DIB_RGB_COLORS );
// init info size
ULONG iBmpInfoSize;
switch( stBmpInfo.bmiHeader.biBitCount )
{
case 24:
iBmpInfoSize = sizeof(BITMAPINFOHEADER);
break;
case 16:
case 32:
iBmpInfoSize = sizeof(BITMAPINFOHEADER)+sizeof(DWORD)*3;
break;
default:
iBmpInfoSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * ( 1 << stBmpInfo.bmiHeader.biBitCount );
break;
}
// copy header
PBITMAPINFO pstBmpInfo = NULL;
if( iBmpInfoSize != sizeof(BITMAPINFOHEADER) )
{
pstBmpInfo = (PBITMAPINFO)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, iBmpInfoSize );
PBYTE pbtBmpInfoDest = (PBYTE)pstBmpInfo;
PBYTE pbtBmpInfoSrc = (PBYTE)&stBmpInfo;
ULONG iSizeTmp = sizeof( BITMAPINFOHEADER );
while( iSizeTmp-- )
*( ( pbtBmpInfoDest )++ ) = *( ( pbtBmpInfoSrc )++ );
}
// create file
HANDLE hFile = CreateFile( sFileName.c_str(), GENERIC_WRITE, 0 , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL );
// init bmp file header
BITMAPFILEHEADER stBmpFileHder = {0};
stBmpFileHder.bfType = 0x4D42; // 'BM'
stBmpFileHder.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + iBmpInfoSize + pstBmpInfo->bmiHeader.biSizeImage;
stBmpFileHder.bfReserved1 = 0;
stBmpFileHder.bfReserved2 = 0;
stBmpFileHder.bfOffBits = sizeof(BITMAPFILEHEADER) + iBmpInfoSize;
// write header to file
DWORD dRet;
WriteFile( hFile, (LPCVOID)&stBmpFileHder, sizeof(BITMAPFILEHEADER), &dRet, NULL );
// allocate size for rest of bmp (body)
PBYTE pBits = (PBYTE)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, stBmpInfo.bmiHeader.biSizeImage );
// get bmp bits
HBITMAP hBmpOld;
HBITMAP hTmpBmp = CreateCompatibleBitmap( hDC, pstBmpInfo->bmiHeader.biWidth, pstBmpInfo->bmiHeader.biHeight );
hBmpOld = (HBITMAP)SelectObject( hDC, hTmpBmp );
GetDIBits( hDC, hBmp, 0, pstBmpInfo->bmiHeader.biHeight, (LPSTR)pBits, pstBmpInfo, DIB_RGB_COLORS );
// write bmp info
WriteFile( hFile, (LPCVOID)pstBmpInfo, iBmpInfoSize, &dRet, NULL );
// write bmp bits
WriteFile( hFile, (LPCVOID)pBits, pstBmpInfo->bmiHeader.biSizeImage, &dRet, NULL );
// release handles and free memory
SelectObject( hDC, hBmpOld );
DeleteObject( hTmpBmp );
CloseHandle( hFile );
GlobalFree( pstBmpInfo );
GlobalFree( pBits );
ReleaseDC( hWnd, hDC );
return true;
}
Thanks!
Since I didn't get any better answer, I simply called a "Draw" function that paints my layered window, onto a temporary HDC.
Meaning I don't copy existing bitmap, but create an identical one, using the same drawing function.
I'd still love to get a better answer for this question.
I need to create a HBITMAP from data returned by a glReadPixels() call:
HDC hCompDC = CreateCompatibleDC(NULL);
HDC hDC = GetDC();
m_hClipboardBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);
if ( m_hClipboardBitmap == NULL )
{
throw runtime_error( "Unable to create bitmap." );
}
HBITMAP hOldBm = (HBITMAP) SelectObject( hCompDC, m_hClipboardBitmap );
int numberOfBytes = 4 * size.cx * size.cy;
unsigned char *pPixelData = new unsigned char[numberOfBytes];
::glReadPixels(minimum.x, minimum.y, size.cx, size.cy, GL_BGRA, GL_UNSIGNED_BYTE, pPixelData);
I tried using:
BITMAPINFOHEADER header;
header.biWidth = size.cx;
header.biHeight = size.cy;
header.biSizeImage = numberOfBytes;
header.biSize = sizeof(BITMAPINFOHEADER);
header.biPlanes = 1;
header.biBitCount = 4 * 8; // RGBA
header.biCompression = 0;
header.biXPelsPerMeter = 0;
header.biYPelsPerMeter = 0;
header.biClrUsed = 0;
header.biClrImportant = 0;
HANDLE handle = (HANDLE)::GlobalAlloc (GHND, sizeof(BITMAPINFOHEADER) + numberOfBytes);
if(handle != NULL)
{
char *pData = (char *) ::GlobalLock((HGLOBAL)handle);
memcpy(pData,&header,sizeof(BITMAPINFOHEADER));
memcpy(pData + sizeof(BITMAPINFOHEADER), pPixelData, numberOfBytes);
::GlobalUnlock((HGLOBAL)handle);
OpenClipboard();
EmptyClipboard();
SetClipboardData(CF_DIB, handle);
CloseClipboard();
}
And that pastes into mspaint OK (so the data is good) but how on earth do I get it into a HBITMAP?!?!
Very old thread, but I wanted to give an answer, at least to keep it as a repository.
void WriteOpenGLPixelsToHBITMAP( HBITMAP dstHBITMAP, HDC dstDC, SIZE dims )
{
BITMAPINFO bitmapInfo;
{
::memset( &bitmapInfo, 0, sizeof( BITMAPINFO ) );
bitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
bitmapInfo.bmiHeader.biWidth = dims.cx;
bitmapInfo.bmiHeader.biHeight = dims.cy;
bitmapInfo.bmiHeader.biSizeImage = dims.cx * dims.cy * 4; // Size 4, assuming RGBA from OpenGL
}
void *bmBits = NULL;
HDC memDC = ::CreateCompatibleDC( dstDC );
HBITMAP memBM = ::CreateDIBSection( NULL, &bitmapInfo, DIB_RGB_COLORS, &bmBits, NULL, 0 );
::glReadPixels( 0,
0,
dims.cx,
dims.cy,
GL_BGRA_EXT,
GL_UNSIGNED_BYTE,
bmBits );
HGDIOBJ prevBitmap = ::SelectObject( memDC, memBM );
HGDIOBJ obj = ::SelectObject( dstDC, dstHBITMAP );
// Remember that OpenGL origin is at bottom, left, but bitmaps are top, left
if ( false == BitBlt( dstDC, 0 /*left*/, dims.cy /*top*/, dims.cx, dims.cy, memDC, 0, 0, SRCCOPY ) )
{
assert( false && "Failed to write pixels to HBitmap from OpenGL glReadPixels" );
}
::SelectObject( memDC, prevBitmap );
::DeleteObject( memBM );
::DeleteDC( memDC );
}
As mentioned, be aware of image being inverted. You can swap SRCCOPY for SRCINVERT. Also you might want to make sure you are copying regions. The code above assumes that the region matches the viewport.
Are you calling the function with the correct parameters. Check the documentation of the function: http://msdn.microsoft.com/en-us/library/dd183491(v=vs.85).aspx. Seems like you have swapped the parameter order and are passing a pointer to a pointer to the data.
-Timo
I am scaling an image down with StretchBlt().
http://img684.imageshack.us/img684/2152/stretchblt.png
As you can see, it currently looks like I have to choose between quality filtering and transparency. Is there any way to get both? This is the only image operation I need to perform, so I'd prefer to avoid extra libraries.
My code:
HDC srcDC = CreateCompatibleDC(NULL);
SelectObject(srcDC, *phbmp);
HDC destDC = CreateCompatibleDC(srcDC);
HBITMAP NewBMP = CreateCompatibleBitmap(srcDC,NewWidth,NewHeight);
SelectObject(destDC,NewBMP);
SetStretchBltMode(destDC,HALFTONE);
SetBrushOrgEx(destDC,0,0,NULL);
if (StretchBlt(destDC,0,0,NewWidth,NewHeight,srcDC,0,0,width,height,SRCCOPY) == TRUE)
{
DeleteObject(*phbmp);
*phbmp = NewBMP;
hr = S_OK;
}
else
DeleteObject(NewBMP);
DeleteDC(srcDC);
DeleteDC(destDC);
Gave up entirely on GDI in the end. Turns out that the proper way of doing this is, of course, with IWICImagingFactory. Final code:
IWICImagingFactory *pImgFac;
hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImgFac));
IWICBitmap* NewBmp;
hr = pImgFac->CreateBitmapFromHBITMAP(*phbmp,0,WICBitmapUseAlpha,&NewBmp);
BITMAPINFO bmi = {};
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth = NewWidth;
bmi.bmiHeader.biHeight = -NewHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
BYTE *pBits;
HBITMAP hbmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void**)&pBits, NULL, 0);
hr = hbmp ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
IWICBitmapScaler* pIScaler;
hr = pImgFac->CreateBitmapScaler(&pIScaler);
hr = pIScaler->Initialize(NewBmp,NewWidth,NewHeight,WICBitmapInterpolationModeFant);
WICRect rect = {0, 0, NewWidth, NewHeight};
hr = pIScaler->CopyPixels(&rect, NewWidth * 4, NewWidth * NewHeight * 4, pBits);
if (SUCCEEDED(hr))
*phbmp = hbmp;
else
DeleteObject(hbmp);
pIScaler->Release();
}
NewBmp->Release();
pImgFac->Release();