How can I take a screenshot and save it as JPEG on Windows?

前端 未结 11 1768
暖寄归人
暖寄归人 2020-12-02 15:11

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\

相关标签:
11条回答
  • 2020-12-02 15:27

    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?

    0 讨论(0)
  • 2020-12-02 15:29

    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()

    0 讨论(0)
  • 2020-12-02 15:32

    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.

    0 讨论(0)
  • 2020-12-02 15:34

    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.

    0 讨论(0)
  • 2020-12-02 15:34

    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.

    0 讨论(0)
  • 2020-12-02 15:36

    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.

    0 讨论(0)
提交回复
热议问题