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\
OK, after a lot of effort here's the answer:
int SaveJpeg(HBITMAP hBmp, LPCWSTR lpszFilename, ULONG uQuality)
{
ULONG *pBitmap = NULL;
CLSID imageCLSID;
EncoderParameters encoderParams;
int iRes = 0;
typedef Status (WINAPI *pGdipCreateBitmapFromHBITMAP)(HBITMAP, HPALETTE, ULONG**);
pGdipCreateBitmapFromHBITMAP lGdipCreateBitmapFromHBITMAP;
typedef Status (WINAPI *pGdipSaveImageToFile)(ULONG *, const WCHAR*, const CLSID*, const EncoderParameters*);
pGdipSaveImageToFile lGdipSaveImageToFile;
// load GdipCreateBitmapFromHBITMAP
lGdipCreateBitmapFromHBITMAP = (pGdipCreateBitmapFromHBITMAP)GetProcAddress(hModuleThread, "GdipCreateBitmapFromHBITMAP");
if(lGdipCreateBitmapFromHBITMAP == NULL)
{
// error
return 0;
}
// load GdipSaveImageToFile
lGdipSaveImageToFile = (pGdipSaveImageToFile)GetProcAddress(hModuleThread, "GdipSaveImageToFile");
if(lGdipSaveImageToFile == NULL)
{
// error
return 0;
}
lGdipCreateBitmapFromHBITMAP(hBmp, NULL, &pBitmap);
iRes = GetEncoderClsid(L"image/jpeg", &imageCLSID);
if(iRes == -1)
{
// error
return 0;
}
encoderParams.Count = 1;
encoderParams.Parameter[0].NumberOfValues = 1;
encoderParams.Parameter[0].Guid = EncoderQuality;
encoderParams.Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParams.Parameter[0].Value = &uQuality;
lGdipSaveImageToFile(pBitmap, lpszFilename, &imageCLSID, &encoderParams);
return 1;
}
what is hModuleThread? Look in here. You can replace with GetModuleHandle()
what is GetEncoderClsid? Look here.
Now the question is, how do I save the encoded pBitmap (as a jpeg) into a BYTE buffer?
Try this at the begining of your code
#include "windows.h"
#include "gdiplus.h"
using namespace Gdiplus;
using namespace Gdiplus::DllExports;
And GdipSaveImageToFile() may compile, in c++ i believe.
In pure C, probably the best is to try to make single includes. Look for the functions declarations in "gdiplus.h" and add minimal includes for each of the functions that do not include namespaces, such as #include "Gdiplusflat.h"
for the GdipSaveImageToFile()
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.
Have you had a look at the FreeImage Project? Yeah, it's an external library, but it's pretty small (~1Mb). Does just about anything you'd want, with images, too. Definitely worth knowing about, even if not for this project.
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:
import screen.jpg
Of course, you could save the above as takeScreenshot.bat and you'd have a programme that matches your requirements.
I had the same problem and solved it with this code. You also need to include gdiplus.lib in the Inputs for the Linker.
struct GdiplusStartupInput
{
UINT32 GdiplusVersion; // Must be 1 (or 2 for the Ex version)
UINT_PTR DebugEventCallback; // Ignored on free builds
BOOL SuppressBackgroundThread; // FALSE unless you're prepared to call the hook/unhook functions properly
BOOL SuppressExternalCodecs; // FALSE unless you want GDI+ only to use its internal image codecs.
};
int __stdcall GdiplusStartup(ULONG_PTR *token, struct GdiplusStartupInput *gstart, struct GdiplusStartupInput *gstartout);
int __stdcall GdiplusShutdown(ULONG_PTR token);
int __stdcall GdipCreateBitmapFromFile(WCHAR *filename, void **GpBitmap);
int __stdcall GdipSaveImageToFile(void *GpBitmap, WCHAR *filename, CLSID *encoder, void *params);
int __stdcall GdipCreateBitmapFromStream(IStream *pstm, void **GpBitmap);
int __stdcall GdipCreateHBITMAPFromBitmap(void *GpBitmap, HBITMAP *bm, COLORREF col);
int __stdcall GdipCreateBitmapFromHBITMAP(HBITMAP bm, HPALETTE hpal, void *GpBitmap);
int __stdcall GdipDisposeImage(void *GpBitmap);
int __stdcall GdipImageGetFrameDimensionsCount(void *GpBitmap, int *count);
int __stdcall GdipImageGetFrameDimensionsList(void *GpBitmap, GUID *guid, int count);
int __stdcall GdipImageGetFrameCount(void *GpBitmap, GUID *guid, int *count);
int __stdcall GdipImageSelectActiveFrame(void *GpBitmap, GUID *guid, int num);
int __stdcall GdipImageRotateFlip(void *GpBitmap, int num);
/*
Save the image memory bitmap to a file (.bmp, .jpg, .png or .tif)
*/
int save_bitmap_to_file(char *filename, HBITMAP hbitmap)
{
wchar_t wstr[MAX_FILENAME];
int sts;
size_t len;
void *imageptr; /* actually an 'image' object pointer */
CLSID encoder;
sts = FAILURE;
_strlwr(filename);
if(strstr(filename, ".png"))
sts = CLSIDFromString(L"{557cf406-1a04-11d3-9a73-0000f81ef32e}", &encoder); /* png encoder classid */
else if(strstr(filename, ".jpg"))
sts = CLSIDFromString(L"{557cf401-1a04-11d3-9a73-0000f81ef32e}", &encoder); /* jpg encoder classid */
else if(strstr(filename, ".jpeg"))
sts = CLSIDFromString(L"{557cf401-1a04-11d3-9a73-0000f81ef32e}", &encoder); /* jpg encoder classid */
else if(strstr(filename, ".tif"))
sts = CLSIDFromString(L"{557cf405-1a04-11d3-9a73-0000f81ef32e}", &encoder); /* tif encoder classid */
else if(strstr(filename, ".bmp"))
sts = CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}", &encoder); /* bmp encoder classid */
else if(strstr(filename, ".gif"))
sts = CLSIDFromString(L"{557cf402-1a04-11d3-9a73-0000f81ef32e}", &encoder); /* gif encoder classid */
if(sts != 0) return(FAILURE);
mbstowcs_s(&len, wstr, MAX_FILENAME, filename, MAX_FILENAME-1); /* get filename in multi-byte format */
sts = GdipCreateBitmapFromHBITMAP(hbitmap, NULL, &imageptr);
if(sts != 0) return(FAILURE);
sts = GdipSaveImageToFile(imageptr, wstr, &encoder, NULL);
GdipDisposeImage(imageptr); /* destroy the 'Image' object */
return(sts);
}
This code could be enhanced by adding a quality value for the jpg file output, the code to do that is higher in this thread.