windows - Et JPEG-kodet skærmbillede til en buffer ved hjælp af GDI + og C ++

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har tilpasset denne kode fra en anden artikel her på SO. Det tager et skærmbillede af skrivebordet og skriver det til en fil med navnet 'test.jpg.'


Jeg er interesseret i at gemme JPEG-data direkte til en buffer, der skal sendes via netværket. Jeg er helt sikker på GdipSaveImageToStream, det er jeg har brug for, men jeg kan ikke regne ud, hvordan det fungerer.]] parameter er særlig forvirrende.


Jeg værdsætter enhver hjælp, du kan give.


#include "stdafx.h"
#include "windows.h"
#include "gdiplus.h"
using namespace Gdiplus;
using namespace Gdiplus::DllExports;

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 = GetDesktopWindow(); // 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
", 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);

        GpImage *bob;
        IStream *ssStr;

        // 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;

}

int \_tmain(int argc, \_TCHAR* argv[])
{
    GetScreeny(L"test.jpg", 75);
    return 0;
}

Bedste reference


Kort svar: Brug IStream-versionen af ​​Gdiplus :: Billede :: Gem. Brug CreateHStreamOnGlobal til at lave et midlertidigt IStream, som du kan konvertere tilbage til en buffer;


Langviklet version med kodeprøve.


Udskift denne linje:


 iRes = (pScreenShot->Save(lpszFilename, &imageCLSID, &encoderParams) == Ok);


Med denne blok kode:


// Note: For the sake of brevity and readability, I'm deliberately not checking
// the return value of any of these calls. In production code, you should do diligent error 
// checking on each function call. Also, there may be an optimization where you can just
// use the memory of the stream itself (by calling GetHGlobalFromStream and GlobalLock)
// But that's an exercise left to the reader.

{
    IStream *pStream = NULL;
    LARGE\_INTEGER liZero = {};
    ULARGE\_INTEGER pos = {};
    STATSTG stg = {};
    ULONG bytesRead=0;
    HRESULT hrRet=S\_OK;

    BYTE* buffer = NULL;  // this is your buffer that will hold the jpeg bytes
    DWORD dwBufferSize = 0;  // this is the size of that buffer;


    hrRet = CreateStreamOnHGlobal(NULL, TRUE, &pStream))
    hrRet = pScreenShot->Save(pStream, &imageCLSID, &encoderParams) == 0 ? S\_OK : E\_FAIL;
    hrRet = pStream->Seek(liZero, STREAM\_SEEK\_SET, &pos);
    hrRet = pStream->Stat(&stg, STATFLAG\_NONAME);

    // allocate a byte buffer big enough to hold the jpeg stream in memory
    buffer = new BYTE[stg.cbSize.LowPart];
    hrRet = (buffer == NULL) ? E\_OUTOFMEMORY : S\_OK;
    dwBufferSize = stg.cbSize.LowPart;

    // copy the stream into memory
    hrRet = pStream->Read(buffer, stg.cbSize.LowPart, &bytesRead);

    // now go save "buffer" and "dwBufferSize" off somewhere.  This is the jpeg buffer
    // don't forget to free it when you are done

    // After success or if any of the above calls fail, don't forget to release the stream
    if (pStream)
    {
        pStream->Release();
    }
}