I'm trying to find a (somewhat) easy way to take a screenshot on window and save the resulting HBITMAP as a JPEG. The tricky part here is that since the code is in C I can't use GDI+ and since the code is a module for a bigger program I can't neither use an external lib (like libjpeg).
This code takes a screenshot and returns a HBITMAP. Saving that bitmap into a file is easy. the problem is that the bitmap is 2 or 3mb.
HDC hDCMem = CreateCompatibleDC(NULL);
HBITMAP hBmp;
RECT rect;
HDC hDC;
HGDIOBJ hOld;
GetWindowRect(hWnd, & rect);
hBmp = NULL;
{
hDC = GetDC(hWnd);
hBmp = CreateCompatibleBitmap(hDC, rect.right - rect.left, rect.bottom - rect.top);
ReleaseDC(hWnd, hDC);
}
hOld = SelectObject(hDCMem, hBmp);
SendMessage(hWnd, WM_PRINT, (WPARAM) hDCMem, PRF_CHILDREN | PRF_CLIENT | PRF_ERASEBKGND | PRF_NONCLIENT | PRF_OWNED);
SelectObject(hDCMem, hOld);
DeleteObject(hDCMem);
return hBmp;
any ideas on how to do this? thanks so much, any help is appreciated
EDIT: Since we went in the direction of GDI+ I thought I'd post the code iv C++ that can take the screenshot and convert it to a JPEG using GDI+. If anyone knows how to achieve this using the FLAT GDI+ i'd appreciate the help. Code:
#include <windows.h>
#include <stdio.h>
#include <gdiplus.h>
using namespace Gdiplus;
int GetEncoderClsid(WCHAR *format, CLSID *pClsid)
{
unsigned int num = 0, size = 0;
GetImageEncodersSize(&num, &size);
if(size == 0) return -1;
ImageCodecInfo *pImageCodecInfo = (ImageCodecInfo *)(malloc(size));
if(pImageCodecInfo == NULL) return -1;
GetImageEncoders(num, size, pImageCodecInfo);
for(unsigned int j = 0; j < num; ++j)
{
if(wcscmp(pImageCodecInfo[j].MimeType, format) == 0){
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
int GetScreeny(LPWSTR lpszFilename, ULONG uQuality) // by Napalm
{
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
HWND hMyWnd = GetForegroundWindow(); // get my own window
RECT r; // the area we are going to capture
int w, h; // the width and height of the area
HDC dc; // the container for the area
int nBPP;
HDC hdcCapture;
LPBYTE lpCapture;
int nCapture;
int iRes;
CLSID imageCLSID;
Bitmap *pScreenShot;
HGLOBAL hMem;
int result;
// get the area of my application's window
//GetClientRect(hMyWnd, &r);
GetWindowRect(hMyWnd, &r);
dc = GetWindowDC(hMyWnd);// GetDC(hMyWnd) ;
w = r.right - r.left;
h = r.bottom - r.top;
nBPP = GetDeviceCaps(dc, BITSPIXEL);
hdcCapture = CreateCompatibleDC(dc);
// create the buffer for the screenshot
BITMAPINFO bmiCapture = {
sizeof(BITMAPINFOHEADER), w, -h, 1, nBPP, BI_RGB, 0, 0, 0, 0, 0,
};
// create a container and take the screenshot
HBITMAP hbmCapture = CreateDIBSection(dc, &bmiCapture,
DIB_PAL_COLORS, (LPVOID *)&lpCapture, NULL, 0);
// failed to take it
if(!hbmCapture)
{
DeleteDC(hdcCapture);
DeleteDC(dc);
GdiplusShutdown(gdiplusToken);
printf("failed to take the screenshot. err: %d\n", GetLastError());
return 0;
}
// copy the screenshot buffer
nCapture = SaveDC(hdcCapture);
SelectObject(hdcCapture, hbmCapture);
BitBlt(hdcCapture, 0, 0, w, h, dc, 0, 0, SRCCOPY);
RestoreDC(hdcCapture, nCapture);
DeleteDC(hdcCapture);
DeleteDC(dc);
// save the buffer to a file
pScreenShot = new Bitmap(hbmCapture, (HPALETTE)NULL);
EncoderParameters encoderParams;
encoderParams.Count = 1;
encoderParams.Parameter[0].NumberOfValues = 1;
encoderParams.Parameter[0].Guid = EncoderQuality;
encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParams.Parameter[0].Value = &uQuality;
GetEncoderClsid(L"image/jpeg", &imageCLSID);
iRes = (pScreenShot->Save(lpszFilename, &imageCLSID, &encoderParams) == Ok);
delete pScreenShot;
DeleteObject(hbmCapture);
GdiplusShutdown(gdiplusToken);
return iRes;
}
You will probably have to use an external library to create a JPEG in C code--there won't be a standard library function to do it. Of course, you could also study the file format and write a function to generate your own, based on the criteria. You could start at wikipedia. If you really can't just use an external library, you could read into the relevant functions of the library to learn about how to generate your own JPEGs. But that sounds like a lot more work than just using the library.
Also, I don't believe the size of a library really comes into play with C -- I believe you only get the size of the functions you call from that library (I think, anyway). Of course if all the functions are tightly coupled than it'd be huge anyway.
One possibility: If you can't modify this program to save as Jpeg, write a second program, using C# / GDI+ / other fancy technologies to monitor the save directory and process saved BMPs into jpegs.
If you can't do that, the Independent Jpeg group has made pure-C Jpeg code available since the late 20th century: A very minimal web page is available here.
Good image compression is hard. There's a reason library code exists for the common formats. It requires a lot of code to do. Your constraints on the problem don't make it sound like there is a practical solution.
There are a couple things to try.
Use an 8-bit per pixel BMP rather than a true color BMP. This assumes that the information loss in color mapping is acceptable, of course. It will buy you a factor of three in file size over a 24-bit BMP.
Try using run length encoding in the BMP you write. RLE is a documented format for BMP, and there should be plenty of sample code out there to make it. It works best on images that have a lot of long runs of identical color. A typical screen without too many gradients and fills fits that description.
If those aren't sufficient, then you might need to resort to a stronger compression. The source code to libjpeg is readily available, and it is possible to statically link it rather than using the DLL. It will require some effort to configure in only the bits you need, but the compression and decompression code are almost completely independent of each other and that splits the library almost in half.
Don't reinvent the wheel.
If what you need is a programme that takes a screen shot and saves it as a jpeg, you can just use ImageMagick's import.
The shell command would simply be:
Of course, you could save the above as takeScreenshot.bat and you'd have a programme that matches your requirements.
libjpeg is free, open source, coded in C. You can copy their code into yours.
http://sourceforge.net/project/showfiles.php?group_id=159521
If you're really concerned about space, you can probably jettison the C runtime libraries as well. If you're only using a few functions, just write your own version (e.g. strcpy). It is possible to write Windows applications that are only a few K bytes. I can send you a small sample app which does this.
If I take my C imaging code and strip it down to just the JPEG encoder, it will probably produce about 20K of code (less if you don't need to support grayscale images).