Thanks for reading this.
I need to draw CBitmap instance which created by bitmap info header and byte array. Because I want them to be able to manipulate it real time in the future. Can't use LoadImage or such APIs that access my hard drive. Memory operations only.
Here's what I've tried.
void CBMPLoaderDlg::drawBitmapFromPath(CString path)
{
CFile file;
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
BYTE* bits = NULL;
CBitmap bm;
CDC *clientDC;
CDC memDC;
try
{
if(! file.Open(path, CFile::modeRead))
throw "File not found";
if(file.Read(&bfh, sizeof(BITMAPFILEHEADER)) < sizeof(BITMAPFILEHEADER))
throw "Not bmp file";
if(file.Read(&bih, sizeof(BITMAPINFOHEADER)) < sizeof(BITMAPINFOHEADER))
throw "Not bmp file";
bits = new BYTE[bih.biSizeImage];
if(file.Read(bits, bih.biSizeImage) < bih.biSizeImage)
throw "Not bmp file";
if(! bm.CreateBitmap(bih.biWidth, bih.biHeight, bih.biPlanes, bih.biBitCount, bits))
throw "Could not create bitmap";
clientDC = this->GetDC();
memDC.CreateCompatibleDC(clientDC);
memDC.SelectObject(bm);
clientDC->BitBlt(0, 0, bih.biWidth, bih.biHeight, &memDC, 0, 0, SRCCOPY);
}
catch(char *e)
{
TRACE("%s\n", e);
}
}
The headers and byte array loads fine. And CBitmap creates fine too. Problem is, Bitblt returns TRUE but no changes on the dialog. Yes, Bitblt just be performed when OnPain() is called. I've tried that and result doesn't vary. I made this way to post this thread.
Am I getting it wrong? Please a little help would be great.
Related
I try to load a bmp to my MFC Picture Control.
void CMFCAppDlg::OnBnClickedButtonload()
{
CFileDialog dlg(TRUE);
int result=dlg.DoModal();
if(result==IDOK)
{
MyBmpFile::Instance() -> setPath (dlg.GetPathName());
UpdateData(FALSE);
}
HANDLE hBitmap = LoadImage(0, MyBmpFile::Instance() -> getPath(), IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
CBitmap m_bitmap;
m_bitmap.Attach((HBITMAP)hBitmap);
CDC dc, *pDC;
BITMAP bmp;
m_bitmap.LoadBitmapW(IDB_BITMAP);
m_bitmap.GetBitmap(&bmp);
pDC = this->GetDC();
dc.CreateCompatibleDC(pDC);
dc.SelectObject(m_bitmap);
pDC->BitBlt(200, 200, bmp.bmWidth, bmp.bmHeight, &dc,0 , 0, SRCCOPY);
m_bitmap.DeleteObject();
m_bitmap.Detach();
}
This code returns me an error after I select an item in dialog box. Problem is with LoadImage() it returns NULL. But actually I dont know what im doing wrong with that.
Ok, I used CImage to draw this bmp, anyway i did not solve the problem with LoadImage(). I try to make it in static way like: L"D:\\e.bmp" or _T("D:\\e.bmp") but even there problem is the same as before.
void CMFCAppDlg::OnBnClickedButtonload()
{
CFileDialog dlg(TRUE);
int result=dlg.DoModal();
if(result==IDOK)
{
MyBmpFile::Instance() -> setPath (dlg.GetPathName());
UpdateData(FALSE);
}
CImage image;
image.Load( MyBmpFile::Instance() ->getPath() );
CDC dc, *pDC;
pDC = this->GetDC();
dc.CreateCompatibleDC(pDC);
image.Draw(pDC -> GetSafeHdc(),0,0);
}
Following may be of help:
INSTANCE hInst = AfxGetInstanceHandle();
HBITMAP hBmp = (HBITMAP)LoadImage(hInst, L"path\to\file.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
Couple of things to check:
Are you using 1-byte or 2-byte characters. Use the L macro for the latter.
Is the path to your file correctly specified (e.g. could be relative to where the program happens to run from)
Can you load the file manually as a bitmap as in the example below?
Code to load a file manually as a bitmap:
CFile file;
if (file.Open(L"C:\\Tmp\\Example.bmp", CFile::modeRead))
{
// Read file header
BITMAPFILEHEADER bmfHeader;
if (file.Read((LPSTR) &bmfHeader, sizeof(bmfHeader)) == sizeof(bmfHeader))
{
// File type should be 'BM'
if (bmfHeader.bfType == ((WORD)('M' << 8)| 'B'))
{
BITMAPINFOHEADER bmiHeader;
if (file.Read((LPSTR) &bmiHeader, sizeof(bmiHeader)) == sizeof(bmiHeader))
{
int width = bmiHeader.biWidth;
int height = bmiHeader.biHeight;
}
}
}
file.Close();
}
I want to be able to display a captured frame from my webcam using the vfw.h library. I have code which captures a single frame and saves it to a Bitmap file. However, I do not know how to then display this Bitmap file within a window. I only know how to display the live video feed within a window. Apologies in advance for the likely obviousness of this question but I have very little experience in this type of programming. Can anyone advise me on how to display the captured frame in another window, for further manipulation, i.e. examples of how to use the relevant macros? I am using C++ in Visual Studio.
ShowWindow(camhwnd,SW_SHOW);
SendMessage(camhwnd,WM_CAP_DRIVER_CONNECT,0,0);
SendMessage(camhwnd, WM_CAP_SET_SCALE, true , 0);
SendMessage(camhwnd, WM_CAP_SET_PREVIEWRATE, 66, 0);
SendMessage(camhwnd, WM_CAP_SET_PREVIEW, true , 0);
SendMessage(camhwnd, WM_CAP_GRAB_FRAME, 0, 0);
//Grab a Frame
SendMessage(camhwnd, WM_CAP_GRAB_FRAME, 0, 0);
//Copy the frame we have just grabbed to the clipboard
SendMessage(camhwnd, WM_CAP_EDIT_COPY,0,0);
//Copy the clipboard image data to a HBITMAP object called hbm
hdc = BeginPaint(camhwnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
if (hdcMem != NULL)
{
if (OpenClipboard(camhwnd))
{
hbm = (HBITMAP) GetClipboardData(CF_BITMAP);
SelectObject(hdcMem, hbm);
GetClientRect(camhwnd, &rc);
CloseClipboard();
}
}
//Save hbm to a .bmp file called Frame.bmp
PBITMAPINFO pbi = CreateBitmapInfoStruct(hwnd, hbm);
CreateBMPFile(hwnd, "Frame.bmp", pbi, hbm, hdcMem);
SendMessage(camhwnd,WM_CAP_DRIVER_CONNECT,0,0);
SendMessage(camhwnd, WM_CAP_SET_SCALE, true , 0);
SendMessage(camhwnd, WM_CAP_SET_PREVIEWRATE, 66, 0);
SendMessage(camhwnd, WM_CAP_SET_PREVIEW, true , 0);
break;
}
}
break;
}
To display a bitmap, you first create a "handled" bitmap object, which results in your holding HBITMAP value which APIs accept for further manipulations, and then you actually display it.
The first step is achieved using CreateBitmap and friends.
On the second, see Displaying HBITMAP.
So I was trying to learn some GDI basics and my code breaks when I try to get the HBITMAP for a png image I want to display...
HBITMAP SplashScreen::LoadPng(WCHAR* path)
{
HBITMAP bmp;
fstream f;
f.open(path);
if(!f.good())
{
throw std::exception("Can't find/read file.");
}
f.close();
Gdiplus::Bitmap* img = Gdiplus::Bitmap::FromFile(path, FALSE);
Gdiplus::Color bg(0,0,0,0);
img->GetHBITMAP(bg, &bmp); // <--- Breaks here! Memory access exception!
return bmp;
}
The code is already so simple, I can't think of anything wrong with it, except maybe I just didn't set something up beforehand??
Thoughts?
Not sure what your problem is, though, I do note that you've got a memory leak.
img is never deleted - you should call delete img; after the call to GetHBITMAP
I use the following (less thorough code) in quick test projects.
// BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF
HBITMAP mLoadImg(WCHAR *szFilename)
{
HBITMAP result=NULL;
Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(szFilename,false);
bitmap->GetHBITMAP(0, &result);
delete bitmap;
return result;
}
I didn't initialize GDI correctly. After fixing my init code, it works fine. That was annoying. Now I know.
was using XP without issue for a long time. switched to 7 and trying to capture screenshots with my previously functioning code no longer works. simple concept and relatively generic code...just find the window that i call and save it as a .png. any ideas what might make this bad boy run again? can't debug with my current setup, but it makes it all the way and spits out the error message after bmp->save(...) ...couldn't save image file. edit: also a file does get created/saved, but it is blank and not written to. perhaps the bitmap encoding or GDI is screwed up?
bool CScreenShot::Snap(CString wintitle, CString file, CString& ermsg)
{
ermsg = ""; // no error message
// create screen shot bitmap
EnumWinProcStruct prm = {0, (LPSTR)(LPCTSTR)wintitle, 0};
// Find the descriptor of the window with the caption wintitle
EnumDesktopWindows(0, EnumWindowsProc, (LPARAM)&prm);
if(!prm.hwnd)
{
ermsg.Format("couldn't find window \"%s\"", wintitle);
return false;
}
// Make the window the topmost window
SetWindowPos(prm.hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
Sleep(300);
// Get device context for the top-level window and client rect
HDC hDC = GetDC(prm.hwnd);
RECT rc;
GetClientRect(prm.hwnd, &rc);
HDC memDC = CreateCompatibleDC(hDC);
// Set the size and color depth for the screen shot image
BITMAPINFO bmpInfo;
memset(&bmpInfo, 0, sizeof(bmpInfo));
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
bmpInfo.bmiHeader.biWidth = rc.right - rc.left;
bmpInfo.bmiHeader.biHeight = rc.bottom - rc.top;
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 24;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth * bmpInfo.bmiHeader.biHeight * 3;
// Create memory buffer and perform a bit-block transfer of the color data from the window to the memory
LPVOID addr;
HBITMAP memBM = CreateDIBSection(memDC, &bmpInfo, DIB_RGB_COLORS, &addr, 0, 0);
HGDIOBJ stdBM = SelectObject(memDC, memBM);
BOOL OK = BitBlt(memDC, 0, 0, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight, hDC, 0, 0, SRCCOPY);
ReleaseDC(prm.hwnd, hDC);
SetWindowPos(prm.hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
if(GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL) != Ok)
{
ermsg.Format("couldn't start GDI+");
return false;
}
// Create a Bitmap object for work with images defined by pixel data from the GDI HBitmap and the GDI HPalette.
Bitmap* bmp = ::new Bitmap(memBM, DIB_RGB_COLORS);
SelectObject(memDC, stdBM);
DeleteObject(memBM);
DeleteDC(memDC);
// Find the encoder for "image/png" mime type
CLSID encoderClsid;
EncoderParameters encoderParameters;
GetEncoderClsid(L"image/png", &encoderClsid);
encoderParameters.Count = 0;
// Convert file name to Unicode (wide-char) string.
WCHAR fn[_MAX_PATH];
MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED, file, file.GetLength() + 1, fn, _MAX_PATH);
// Save the screen shot into the specified file using image encoder with the mime style "image/png"
if(bmp->Save(fn, &encoderClsid, &encoderParameters) != Ok)
{
ermsg.Format("couldn't save image file \"%s\"", file);
return false;
}
::delete bmp;
GdiplusShutdown(gdiplusToken);
return true;
}
The error message implies that you're trying to save the file to a folder that you don't have permission to write to. Many folders such as Program Files are now protected. Since you didn't include the path in your sample code I'm unable to determine if this is the actual problem.
Edit: Another possibility is that the Bitmap is improperly constructed which causes the Save to fail. The second parameter to the constructor is supposed to be a handle to a palette, I think DIB_RGB_COLORS would be invalid here and you should use NULL. Also there are a couple of caveats noted in the Microsoft documentation and perhaps the different OS versions react differently when you break the rules:
You are responsible for deleting the GDI bitmap and the GDI palette. However, you should not delete the GDI bitmap or the GDI palette until after the GDI+ Bitmap::Bitmap object is deleted or goes out of scope.
Do not pass to the GDI+ Bitmap::Bitmap constructor a GDI bitmap or a GDI palette that is currently (or was previously) selected into a device context.
win7 won't accept encoderParameters.Count == 0 for some reason. Set that == 1 and you should be all set.
you probably could also just remove that parameter from Save() (overloaded)
I have a bitmap of large dimension (2000 x 2000) i need to shrink that bitmap to a small dimension (150 x 150). i have written a code for it, but its not working. Can anybody help in finding the problem? The problem is the destination bitmap is just blank. I am selecting wrong DC's? I have made sure that both the source and destinations are correct. After doing bitblt do i need to do some more thing to destination bitmap?
BOOL ReSizeBitmap(CBitmap *pBitmap, CBitmap *pNewBitmap)
{
// Get new bitmap size
BITMAP bmOld;
if( !pBitmap->GetBitmap(&bmOld) )
{
return FALSE;
}
CRect rcPrev(0, 0, bmOld.bmWidth, bmOld.bmHeight);
int newWidth = 150;
int newHeight = 150;
if( newWidth < 1 || newHeight < 1 )
{
::SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
BOOL bResult = FALSE;
try
{
CDC dcDest;
CDC dcSource;
dcSource.CreateCompatibleDC(NULL);
dcDest.CreateCompatibleDC(NULL);
CBitmap* pSourceOld = dcSource.SelectObject(pBitmap);
CBitmap* pDestold = dcDest.SelectObject(pNewBitmap);
if( !pNewBitmap->CreateCompatibleBitmap(
&dcDest, newWidth, newHeight) )
{
return FALSE;
}
int oldStretchMode = dcDest.SetStretchBltMode(HALFTONE);
bResult = dcDest.StretchBlt(
0, 0, 150, 150,
&dcSource, 0, 0, bmOld.bmWidth, bmOld.bmHeight,
SRCCOPY);
dcDest.SetStretchBltMode(oldStretchMode);
dcSource.SelectObject(pSourceOld);
dcDest.SelectObject(pDestold);
bResult = TRUE;
}
catch(CResourceException* /*e*/)
{
}
return bResult;
}
Well even if the code works there is some cleaniing up to do.
RAII is one of the idiom you realy need when you are working in MFC!
if( !pNewBitmap->CreateCompatibleBitmap(&dcDest, newWidth, newHeight) )
{
return FALSE;
}
When you return FALSE or there is an exception you haven't called
cSource.SelectObject(pSourceOld);
dcDest.SelectObject(pDestold);
to cleanup before you left the function.
Create a small helper class to cleanup all the time, you don't have to worry about return or throw statements.
class SelectObjectAndCleanUp
{
CDC& deviceContext;
CBitmap *const oldSource;
public:
SelectObjectCleanUp( CDC& deviceContext, CBitmap* source )
: deviceContext(deviceContext),
oldSource( deviceContext.SelectObject(source) ) {
}
~SelectObjectCleanUp() {
deviceContext.SelectObject(oldSource)
}
};
// use of the helper
SelectObjectCleanUp sourceSelectionAndCleanup(dcSource, pBitmap );
SelectObjectCleanUp destionationSelectionAndCleanup(dcDest, pNewBitmap );
I'm not too familiar with C++, but are you selecting the new bitmap into your new DC before it's created? Also, when you call CreateCompatibleBitmap, I think you want to use your screen DC (the one you used to create the destination DC), not your compatible memory DC. So, get the screen DC with GetDC, and pass that into both CreateCompatibleDC AND CreateCompatibleBitmap.
There is a great free C++ image library called CxImage that is open source under the zlib license.
No need to reinvent the wheel.
Download http://www.gdiwatch.com/, it may show you where the error is (it can be made to work with vs 2008, too - just copy the registry keys manually from the vs2005 to vs2008 directories)
Have you tried doing the CreateCompatibleBitmap() before the SelectObject() call?
Does GetBitmap() of the new bitmap return the correct size, i.e. is the new bitmap valid?
The rest seem ok, it should work like this I believe. Does it work with other StretchBltModes?
Thanks to all of the guys who peeped and suggested solutions, any how after some debugging i found the problem. Here is the solution!!!
CBitmap *SrcBmp;
HBITMAP hBmp;
hBmp= (HBITMAP)LoadImage( NULL, L"c:\\source.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE );
SrcBmp = CBitmap::FromHandle(hBmp);
BITMAP BmpInfo;
SrcBmp->GetBitmap(&BmpInfo);
CDC SrcDC;
SrcDC.CreateCompatibleDC(NULL);
CBitmap DestBmp;
DestBmp.CreateCompatibleBitmap(&SrcDC,150,150);
CDC DestDC;
DestDC.CreateCompatibleDC(NULL);
CBitmap *pOldBmp1 = SrcDC.SelectObject(SrcBmp);
CBitmap *pOldBmp2 = DestDC.SelectObject(&DestBmp);
DestDC.StretchBlt(0,0,150,150,&SrcDC,0,0,BmpInfo.bmWidth,BmpInfo.bmHeight,SRCCOPY);
CImage image;
image.Attach(DestBmp);
image.Save(_T("C:\\test.bmp"), Gdiplus::ImageFormatBMP);
SrcDC.SelectObject(pOldBmp1);
DestDC.SelectObject(pOldBmp2);