Contents |
Related Articles |
Capturing the Screen: Discusses the techniques of capturing the screen using GDI, DirectX, Windows Media API Create Movie From HBitmap: Introduces the classes for generating movies (avi / wmv / quicktime) from a sequence of HBitmaps Recording DirectX and OpenGL Animations: Introduces the classes for generating movies from DirectX and OpenGL Animations CFugue Runtime for MIDI Score Programming: C++ Library for Programming Music Notes |
While Developing some applications like Movie Player or any simple game using DirectDraw, some times the need to provide an option for saving the screen-shot may arise. So, it is better for an application to provide such an option if it feels appropriate.
Saving the contents of the DirectDraw Surface requires the following Steps:
Be careful not to delete the bitmap object which is sent to Clipboard.
These Steps Can be sublimated into piece of code as follows:
void CopySurface(IDirectDrawSurface *pDDSurface) { HDC hdc, hmemdc ; HBITMAP hbitmap, hprevbitmap; DDSURFACEDESC ddsd; pDDSurface->GetDC(&hdc); hmemdc = CreateCompatibleDC(hdc); ZeroMemory(&ddsd ,sizeof( ddsd )); // better to clear before using ddsd.dwSize = sizeof( ddsd ); //initialize with size pDDSurface->GetSurfaceDesc(&ddsd); hbitmap = CreateCompatibleBitmap( hdc ,ddsd.dwWidth ,ddsd.dwHeight); hprevbitmap = (HBITMAP) SelectObject( hmemdc, hbitmap ); BitBlt(hmemdc,0 ,0 ,ddsd.dwWidth ,ddsd.dwHeight ,hdc ,0 ,0,SRCCOPY); if( OpenClipboard ( hWnd ) ) //hWnd == Handle to App Window { EmptyClipboard(); SetClipboardData(CF_BITMAP,hbitmap); CloseClipboard(); } else MessageBox(hWnd,"Clipboard not opened","Error",MB_OK); SelectObject(hmemdc,hprevbitmap); // restore the old bitmap DeleteDC(hmemdc); pDDSurface->ReleaseDC(hdc); return ; }
Pass the DirectDraw Surface Pointer as the parameter to this function to copy the contents to clipboard.
In case if OffScreenSurface and PrimaryScreenSurface are being used for blitting operations , and if they vary in size ,like in my MoviePlayer Application , the OffScreenSurface can be passed as parameter to copy the bitmap with original size and PrimaryScreenSurface can be Passed as parameter to copy the bitmap with enlarged size, normally the size of output.
Be Aware of the fact that this is not saving the contents of surface to the secondary storage .So, the clipboard image would be lost , if it is not saved before any new images are copied to the clipboard by any other application or the same application. Because the clipboard is unique to the entire system and works on sharing of Global Memory.
The Copied images can be saved by pasting them into new image files ,using any imaging software, like PaintBrush . This Code Comes Handy in Saving Some Random Screenshots. This can be further developed to save the surface directly to disk as bitmap file.
The following code can be used to save the bitmap onto the disk.
void SaveBitmap(char *szFilename,HBITMAP hBitmap) { HDC hdc=NULL; FILE* fp=NULL; LPVOID pBuf=NULL; BITMAPINFO bmpInfo; BITMAPFILEHEADER bmpFileHeader; do{ hdc=GetDC(NULL); ZeroMemory(&bmpInfo,sizeof(BITMAPINFO)); bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); GetDIBits(hdc,hBitmap,0,0,NULL,&bmpInfo,DIB_RGB_COLORS); if(bmpInfo.bmiHeader.biSizeImage<=0) bmpInfo.bmiHeader.biSizeImage=bmpInfo.bmiHeader.biWidth*abs(bmpInfo.bmiHeader.biHeight)*(bmpInfo.bmiHeader.biBitCount+7)/8; if((pBuf = malloc(bmpInfo.bmiHeader.biSizeImage))==NULL) { MessageBox( NULL, "Unable to Allocate Bitmap Memory", "Error", MB_OK|MB_ICONERROR); break; } bmpInfo.bmiHeader.biCompression=BI_RGB; GetDIBits(hdc,hBitmap,0,bmpInfo.bmiHeader.biHeight,pBuf, &bmpInfo, DIB_RGB_COLORS); if((fp = fopen(szFilename,"wb"))==NULL) { MessageBox( NULL, "Unable to Create Bitmap File", "Error", MB_OK|MB_ICONERROR); break; } bmpFileHeader.bfReserved1=0; bmpFileHeader.bfReserved2=0; bmpFileHeader.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+bmpInfo.bmiHeader.biSizeImage; bmpFileHeader.bfType='MB'; bmpFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp); fwrite(&bmpInfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp); fwrite(pBuf,bmpInfo.bmiHeader.biSizeImage,1,fp); }while(false); if(hdc) ReleaseDC(NULL,hdc); if(pBuf) free(pBuf); if(fp) fclose(fp); }
The Function GetDIBits() retrieves the bits of the specified bitmap and copies them into the specified buffer using the specified format. However, in place of the buffer if we specify NULL, the function returns the dimension and the format of bitmap in the supplied BITMAPINFO structure. In the above code we are calling the GetDIBits() function two times. In the first call we are using NULL pointer for the buffer so that we can get the dimensions of the bitmap. Once we get the size of the bitmap we allocate the memory to hold the contents of the bitmap and then call the GetDIBits() for the second time using the allocated buffer. This time GetDIBits() would retrieve the bitmap bits and would place them into the buffer which we can write into the file at the specified path using fwrite(). However, for other applications to recognize the file as a valid bitmap file we should insert proper headers at the beginning of the file. The BITMAPFILEHEADER and BITMAPINFOHEADER are the two headers that are required to be present for any valid bitmap file. Filling these headers is simple and straight forward as presented above. Once we are done with the writing we free the allocated memory using the free() function.
Note that the bitmap identified by hBitmap parameter must not be selected into a device context when the application calls this function. In case you have the bitmap selected into any device context - first unselect it before calling the above function by selecting some temporary bitmap into the device context, then call the above function and when returned from the function select it back into the device context. This way you could save any bitmap to the disk in any application.
By
P.GopalaKrishna