This WebCam Tutorial will guide you through connecting a webcam in Microsoft Windows and grabbing a frame from the camera so we can later
save it to a bitmap file called "Frame.bmp".
First of all this is a c++ win32 Tutorial so create an empty Win32 application.
1. Setting up some defines.
First of all we need to set up some defines that we need to connect to a webcam. If you are using Microsoft’s Visual Studio Pro edition you wont need them as they are included in Microsoft’s version of vfw.h, if you are
using something else like Code::Blocks as I am then you will need them.
I recommend storing them in a header but I will put them in the main .cpp.
They are basically just values.
#define WM_CAP_START 0x0400 #define WM_CAP_DRIVER_CONNECT (WM_CAP_START + 10) #define WM_CAP_DRIVER_DISCONNECT (WM_CAP_START + 11) #define WM_CAP_EDIT_COPY (WM_CAP_START + 30) #define WM_CAP_GRAB_FRAME (WM_CAP_START + 60) #define WM_CAP_SET_SCALE (WM_CAP_START + 53) #define WM_CAP_SET_PREVIEWRATE (WM_CAP_START + 52) #define WM_CAP_SET_PREVIEW (WM_CAP_START + 50) #define WM_CAP_DLG_VIDEOSOURCE (WM_CAP_START + 42) #define WM_CAP_STOP (WM_CAP_START+ 68)
2. Include Files.
We need to include two header files the first is windows.h
which is just the standard windows header. The other is vfw.h
which stands for video for windows and is where the webcam stuff is.
You will need to link to vfw32 Library in your projects linker settings.
For Code::Blocks this is done through Project -> Build Options ->
Linker Settings and click the add button and type -> vfw32.
At the same time you might as well add gdi32 as well.
For Microsofts Visual C++ Pro or Express Edition
its Project -> properties or Alt+F7.
Configuration Properties -> Linker -> Input -> Additional Dependencies
type in Vfw32.lib; and check to make sure gdi32.lib; is also there
in the dependencies.
If you are using the express Edition you will have to download the
windows sdk. Found here at ->
Link to SDK
It requires Windows XP SP3 and above.
Click the link to download it.
And open up windows explorer and go to ->
Program Files\Microsoft SDKs\Windows\v7.0A
there you should see a bin folder, a lib folder and an include folder.
Open the include folder and select Edit -> Select All and then Edit -> Copy.
Then go to your compilers include folder typically Program Files ->
Microsoft Visual Studio 10.0-> VC
there you should see another include folder open it and paste what you have copied into it.
This will copy the Header required for this program and all other headers
which you might like to have into it.
Go back to Program Files\Microsoft SDKs\Windows\v7.0A
and open the lib folder, select Edit -> Select All and then Edit -> Copy.
Go to Program Files\ Microsoft Visual Studio 10.0\ VC
and open the lib folder and paste what you have copied into it.
This will copy a host of very good libraries one of which is called
Vfw32.lib. Which we will use in our Webcam Program.
If you are using Microsoft Visual C++ Pro or Express Edition
there is one more thing you will have to do because this code is not
a unicode build.
Go to Project -> Properties-> Configuration Properties -> General
click on Character Set and change it to use Multi-Byte Character Set.
#include <windows.h> #include <vfw.h>
3. Procedures.
We will use 3 procedures the first is WindowProc.
The second is CreateBitmapInfoStruct which is a PBITMAPINFO
structure which contains information on color and dimension in a windows based DIB.
The third and last procedure is CreateBMPFile which creates and saves
the bitmap as a .bmp file.
LRESULT CALLBACK WindowProc (HWND, UINT, WPARAM, LPARAM); PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp); void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC);
4. Setting up the window.
Next we need to setup some variables and a window itself.
char szAppName [] = TEXT("WebCam");
HWND camhwnd;
HDC hdc ;
HDC hdcMem;
PAINTSTRUCT ps;
HBITMAP hbm;
RECT rc;
//WinMain -- Main Window
int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
HWND hwnd;
MSG msg;
WNDCLASS wc;
wc.style = CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(GetModuleHandle(NULL), IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szAppName;
RegisterClass (&wc);
// Create the window
hwnd = CreateWindow (szAppName,szAppName,WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,CW_USEDEFAULT,CW_USEDEFAULT,300,600,0,0,hInstance,0);
ShowWindow (hwnd,SW_SHOW);
UpdateWindow (hwnd);
while (GetMessage(&msg,0,0,0))
{
if(!IsDialogMessage(hwnd, &msg))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
return msg.wParam;
}
5. WindowProc.
First of all we need to set up three buttons.
These are
1. A start camera button.
2. A stop camera button.
3. A button to grab a frame from the camera.
Secondly we need to set up a Camera window
this is called camhwnd and is created by using
the capCreateCaptureWindow command this must include
the Window style WS_CHILD.
Third we need to pick a webcam from the Video source Dialog
WM_CAP_DLG_VIDEOSOURCE.
We do this by sending a message to our camera window camhwnd.
SendMessage(camhwnd,WM_CAP_DRIVER_CONNECT,0,0);
SendMessage(camhwnd,WM_CAP_DLG_VIDEOSOURCE,0,0);
There is another way to do this by enumerating all webcam drivers
on the system but for simplicity I wont be doing this in this tutorial
I may write another tutorial on how to do this at a later date.
Fourth come the Window Message Commands
1. Case 1
This connects and shows our webcam picture.
WM_CAP_SET_SCALE is set to true to enable the webcam
picture to be resized to the dimensions of our camera window.
WM_CAP_SET_PREVIEWRATE is set to 66 frames per second.
To provide a reasonable moving picture without too much demand on resources.
WM_CAP_SET_PREVIEW is set to true so a picture will be drawn on the camera window camhwnd.
2. Case 2
Calls WM_CAP_DRIVER_DISCONNECT to amazingly enough
disconnect the webcam.
3. Case 3
This grabs the frame and saves it to a .bmp file called "Frame.bmp"
WM_CAP_GRAB_FRAME does the frame grabbing this freezes the camera output so we will need to restart it later on.
WM_CAP_EDIT_COPY copies our grabbed frame to the clipboard.
Then we copy the clipboard image data to a HBITMAP object called
‘hbm’.
We then create and save the bitmap file itself using ‘hbm’ and the function call CreateBMPFile(hwnd, "Frame.bmp", pbi, hbm, hdcMem);
.
4. Case WM_DESTROY
Its important we disconnect the webcam if the user closes the window.
//Main Window Procedure WindowProc
LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HINSTANCE hInstance = GetModuleHandle(NULL);
//some buttons
HWND hButtStartCam;
HWND hButtStopCam;
HWND hButtGrabFrame;
switch (message) /* handle the messages */
{
case WM_CTLCOLORSTATIC:
SetBkMode(hdc,TRANSPARENT);
return (LRESULT)CreateSolidBrush(0xFFFFFF);
case WM_CREATE:
{
hButtStartCam = CreateWindowEx(0,"BUTTON","Start Camera",WS_CHILD | WS_VISIBLE,
0,0,100,20,hwnd,(HMENU)1,hInstance, 0);
hButtStopCam = CreateWindowEx(0,"BUTTON","Stop Camera",WS_CHILD | WS_VISIBLE,
0,25,100,20,hwnd,(HMENU)2,hInstance, 0);
hButtGrabFrame = CreateWindowEx(0,"BUTTON","Grab Frame",WS_CHILD | WS_VISIBLE,
0,50,100,20,hwnd,(HMENU)3,hInstance, 0);
camhwnd = capCreateCaptureWindow ("camera window", WS_CHILD , 0, 100, 300, 300, hwnd, 0);
SendMessage(camhwnd,WM_CAP_DRIVER_CONNECT,0,0);
SendMessage(camhwnd,WM_CAP_DLG_VIDEOSOURCE,0,0);
break;
}
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case 1:
{
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);
break;
}
case 2:
{
ShowWindow(camhwnd,SW_HIDE);
SendMessage(camhwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
break;
}
case 3:
{
//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;
}
case WM_DESTROY:
{
SendMessage(camhwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
PostQuitMessage(0); /* send a WM_QUIT to the message queue */
break;
}
default: /* for messages that we don't deal with */
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
6. Procedures to Create and Save a Bitmap.
First up is CreateBitmapInfoStruct
which creates a PBITMAPINFO structure it takes two
arguments HWND and an HBITMAP object.
A DIB consists of two distinct parts: a BITMAPINFO structure describing the dimensions and colors of the bitmap, and an array of bytes defining the pixels of the bitmap. The bits in the array are packed together, but each scan line must be padded with zeroes to end on a LONG data-type boundary. If the height of the bitmap is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. If the height is negative, the bitmap is a top-down DIB and its origin is the upper left corner.
A bitmap is packed when the bitmap array immediately follows the BITMAPINFO header. Packed bitmaps are referenced by a single pointer. For packed bitmaps, the biClrUsed member must be set to an even number when using the BI_RGB mode . BI_RGB means that the bitmap is uncompressed.
Secondly we use CreateBMPFile to save the bitmap
it takes 5 arguments the hwnd which owns the bitmap, the filename
in this case "Frame.bmp" a PBITMAPINFO structure which we have just went over, a HBITMAP object and a HDC, handle to device context.
First of all is pbih which is a PBITMAPINFOHEADER
this structure contains information about the dimensions and color format of a DIB.
lpBits contains the size, in bytes, of the image and we allocate some memory for it. Next up we use GetDIBits to retrieve the color table and the bits from the DIB. This function retrieves the bits of the specified compatible bitmap and copies them into a buffer as a DIB using the specified format.
In this case DIB_RGB_COLORS.
Next we create the file using CreateFile the CREATE_ALWAYS
parameter Creates a new file, always.
If the specified file exists and is writable, the function overwrites the file, the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS.
Next up is the BITMAPFILEHEADER hdr.
It is important to note that hdr.bfType has to have the ‘BM’ designator in it which is made up of 0x4d42 which is B = 0x42 and M = 0x4d.
The bfSize member specifies the size, in bytes, of the bitmap file.
hdr.bfReserved1and hdr.bfReserved2 must always be set to zero.
BfOffBits Specifies the offset, in bytes, from the BITMAPFILEHEADER structure to the bitmap bits.
Next we copy the BITMAPFILEHEADER into the .BMP file
with WriteFile
Next we copy the BITMAPINFOHEADER and RGBQUAD array into the file.
With WriteFile
Next we copy the array of color indices into the .BMP file
with WriteFile
Last of all we close the .BMP file with CloseHandle
and free the memory we allocated for lpbits
void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC)
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;
pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)
{
MessageBox(hwnd,"GlobalAlloc","Error", MB_OK );
}
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,DIB_RGB_COLORS))
{
MessageBox(hwnd,"GetDIBits","Error",MB_OK );
}
// Create the .BMP file.
hf = CreateFile(pszFile,GENERIC_READ | GENERIC_WRITE,(DWORD) 0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,(HANDLE) NULL);
if (hf == INVALID_HANDLE_VALUE)
{
MessageBox( hwnd,"CreateFile","Error", MB_OK);
}
hdr.bfType = 0x4d42; // File type designator "BM" 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL) )
{
MessageBox(hwnd,"WriteFileHeader","Error",MB_OK );
}
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), (LPDWORD) &dwTmp, NULL))
{
MessageBox(hwnd,"WriteInfoHeader","Error",MB_OK );
}
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
{
MessageBox(hwnd,"WriteFile","Error",MB_OK );
}
// Close the .BMP file.
if (!CloseHandle(hf))
{
MessageBox(hwnd,"CloseHandle","Error",MB_OK );
}
// Free memory.
GlobalFree((HGLOBAL)lpBits);
}
PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
// Retrieve the bitmap color format, width, and height.
if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
{
MessageBox(hwnd,"GetObject","Error",MB_OK );
}
// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits != 24)
{
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits));
}
// There is no RGBQUAD array for the 24-bit-per-pixel format.
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
{
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
}
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// For Windows NT, the width must be DWORD aligned unless
// the bitmap is RLE compressed. This example shows this.
// For Windows 95/98/Me, the width must be WORD aligned unless the
// bitmap is RLE compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 * pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi; //return BITMAPINFO
}
And Finally here is the full code for connecting a webcam in windows
and grabbing a frame and saving it to the hard drive.
//Setup some defines you need to connect to a webcam
//You can put these in a header
//If you are using Microsoft Visual Studio you wont need them as they
//Are included in Microsoft's version of vfw.h
#define WM_CAP_START 0x0400
#define WM_CAP_DRIVER_CONNECT (WM_CAP_START + 10)
#define WM_CAP_DRIVER_DISCONNECT (WM_CAP_START + 11)
#define WM_CAP_EDIT_COPY (WM_CAP_START + 30)
#define WM_CAP_GRAB_FRAME (WM_CAP_START + 60)
#define WM_CAP_SET_SCALE (WM_CAP_START + 53)
#define WM_CAP_SET_PREVIEWRATE (WM_CAP_START + 52)
#define WM_CAP_SET_PREVIEW (WM_CAP_START + 50)
#define WM_CAP_DLG_VIDEOSOURCE (WM_CAP_START + 42)
#define WM_CAP_STOP (WM_CAP_START+ 68)
//End of defines
#include <windows.h>
#include <vfw.h>
//Remember to Link to vfw32 Library, gdi32 Library
LRESULT CALLBACK WindowProc (HWND, UINT, WPARAM, LPARAM);
PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp);
void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC);
char szAppName [] = TEXT("WebCam");
HWND camhwnd;
HDC hdc ;
HDC hdcMem;
PAINTSTRUCT ps;
HBITMAP hbm;
RECT rc;
//WinMain -- Main Window
int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
HWND hwnd;
MSG msg;
WNDCLASS wc;
wc.style = CS_HREDRAW|CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(GetModuleHandle(NULL), IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szAppName;
RegisterClass (&wc);
// Create the window
hwnd = CreateWindow (szAppName,szAppName,WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,CW_USEDEFAULT,CW_USEDEFAULT,300,600,0,0,hInstance,0);
ShowWindow (hwnd,SW_SHOW);
UpdateWindow (hwnd);
while (GetMessage(&msg,0,0,0))
{
if (!IsDialogMessage(hwnd, &msg))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
return msg.wParam;
}
//Main Window Procedure WindowProc
LRESULT CALLBACK WindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HINSTANCE hInstance = GetModuleHandle(NULL);
//some buttons
HWND hButtStartCam;
HWND hButtStopCam;
HWND hButtGrabFrame;
switch (message) /* handle the messages */
{
case WM_CTLCOLORSTATIC:
SetBkMode(hdc,TRANSPARENT);
return (LRESULT)CreateSolidBrush(0xFFFFFF);
case WM_CREATE:
{
hButtStartCam = CreateWindowEx(0,"BUTTON","Start Camera",WS_CHILD | WS_VISIBLE,
0,0,100,20,hwnd,(HMENU)1,hInstance, 0);
hButtStopCam = CreateWindowEx(0,"BUTTON","Stop Camera",WS_CHILD | WS_VISIBLE,
0,25,100,20,hwnd,(HMENU)2,hInstance, 0);
hButtGrabFrame = CreateWindowEx(0,"BUTTON","Grab Frame",WS_CHILD | WS_VISIBLE,
0,50,100,20,hwnd,(HMENU)3,hInstance, 0);
camhwnd = capCreateCaptureWindow ("camera window", WS_CHILD , 0, 100, 300, 300, hwnd, 0);
SendMessage(camhwnd,WM_CAP_DRIVER_CONNECT,0,0);
SendMessage(camhwnd,WM_CAP_DLG_VIDEOSOURCE,0,0);
break;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case 1:
{
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);
break;
}
case 2:
{
ShowWindow(camhwnd,SW_HIDE);
SendMessage(camhwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
break;
}
case 3:
{
//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;
}
case WM_DESTROY:
{
SendMessage(camhwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
PostQuitMessage(0); /* send a WM_QUIT to the message queue */
break;
}
default: /* for messages that we don't deal with */
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC)
{
HANDLE hf; // file handle
BITMAPFILEHEADER hdr; // bitmap file-header
PBITMAPINFOHEADER pbih; // bitmap info-header
LPBYTE lpBits; // memory pointer
DWORD dwTotal; // total count of bytes
DWORD cb; // incremental count of bytes
BYTE *hp; // byte pointer
DWORD dwTmp;
pbih = (PBITMAPINFOHEADER) pbi;
lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
if (!lpBits)
{
MessageBox(hwnd,"GlobalAlloc","Error", MB_OK );
}
// Retrieve the color table (RGBQUAD array) and the bits
// (array of palette indices) from the DIB.
if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi,DIB_RGB_COLORS))
{
MessageBox(hwnd,"GetDIBits","Error",MB_OK );
}
// Create the .BMP file.
hf = CreateFile(pszFile,GENERIC_READ | GENERIC_WRITE,(DWORD) 0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,(HANDLE) NULL);
if (hf == INVALID_HANDLE_VALUE)
{
MessageBox( hwnd,"CreateFile","Error", MB_OK);
}
hdr.bfType = 0x4d42; // File type designator "BM" 0x42 = "B" 0x4d = "M"
// Compute the size of the entire file.
hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
// Compute the offset to the array of color indices.
hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD);
// Copy the BITMAPFILEHEADER into the .BMP file.
if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, NULL) )
{
MessageBox(hwnd,"WriteFileHeader","Error",MB_OK );
}
// Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), (LPDWORD) &dwTmp, NULL))
{
MessageBox(hwnd,"WriteInfoHeader","Error",MB_OK );
}
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp,NULL))
{
MessageBox(hwnd,"WriteFile","Error",MB_OK );
}
// Close the .BMP file.
if (!CloseHandle(hf))
{
MessageBox(hwnd,"CloseHandle","Error",MB_OK );
}
// Free memory.
GlobalFree((HGLOBAL)lpBits);
}
PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
{
BITMAP bmp;
PBITMAPINFO pbmi;
WORD cClrBits;
// Retrieve the bitmap color format, width, and height.
if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp))
{
MessageBox(hwnd,"GetObject","Error",MB_OK );
}
// Convert the color format to a count of bits.
cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
if (cClrBits == 1)
cClrBits = 1;
else if (cClrBits <= 4)
cClrBits = 4;
else if (cClrBits <= 8)
cClrBits = 8;
else if (cClrBits <= 16)
cClrBits = 16;
else if (cClrBits <= 24)
cClrBits = 24;
else cClrBits = 32;
// Allocate memory for the BITMAPINFO structure. (This structure
// contains a BITMAPINFOHEADER structure and an array of RGBQUAD
// data structures.)
if (cClrBits != 24)
{
pbmi = (PBITMAPINFO) LocalAlloc(LPTR,sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< cClrBits));
}
// There is no RGBQUAD array for the 24-bit-per-pixel format.
else
pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
// Initialize the fields in the BITMAPINFO structure.
pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth = bmp.bmWidth;
pbmi->bmiHeader.biHeight = bmp.bmHeight;
pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
if (cClrBits < 24)
{
pbmi->bmiHeader.biClrUsed = (1<<cClrBits);
}
// If the bitmap is not compressed, set the BI_RGB flag.
pbmi->bmiHeader.biCompression = BI_RGB;
// Compute the number of bytes in the array of color
// indices and store the result in biSizeImage.
// For Windows NT, the width must be DWORD aligned unless
// the bitmap is RLE compressed. This example shows this.
// For Windows 95/98/Me, the width must be WORD aligned unless the
// bitmap is RLE compressed.
pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 * pbmi->bmiHeader.biHeight;
// Set biClrImportant to 0, indicating that all of the
// device colors are important.
pbmi->bmiHeader.biClrImportant = 0;
return pbmi; //return BITMAPINFO
}






MultiQuote





|