2016-08-01 10 views
2

GDIまたはDirectXのいずれかの方法を使用して、Windowsでスクリーンキャプチャを行うための多数の投稿があることは知っています。しかし、キャプチャした画像をビットマップに保存していたのに対し、代わりにバッファに保存したいと思っていました。ここに私のコードは、GDIの方法で行うことです。Windowsで4K画面をキャプチャしてバッファに直接保存する

HWND hwind = GetDesktopWindow(); 
HDC hdc = GetDC(hwind); 

uint32_t resx = GetSystemMetrics(SM_CXSCREEN); 
uint32_t resy = GetSystemMetrics(SM_CYSCREEN); 
uint32_t BitsPerPixel = GetDeviceCaps(hdc, BITSPIXEL); 
HDC hdc2 = CreateCompatibleDC(hdc); 

BITMAPINFO info; 
info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
info.bmiHeader.biWidth = resx; 
info.bmiHeader.biHeight = resy; 
info.bmiHeader.biPlanes = 1; 
info.bmiHeader.biBitCount = BitsPerPixel; 
info.bmiHeader.biCompression = BI_RGB; 

void *data; 
static HBITMAP hbitmap = CreateDIBSection(hdc2, &info, DIB_RGB_COLORS, 
    (void**)&data, 0, 0); 
SelectObject(hdc2, hbitmap); 
BitBlt(hdc2, 0, 0, resx, resy, hdc, 0, 0, SRCCOPY); 

uint8_t *ptr = new uint8_t[4 * resx * resy]; 
uint32_t lineSizeSrc = 4 * resx;  // not always correct 
uint32_t linesizeDst = 4 * resx; 
for (uint32_t y = 0; y < resy; y++) 
    memcpy(ptr + y * lineSizeDst, 
     (uint8_t*) data + y * lineSizeSrc, 
     lineSizeDst); 

DeleteObject(hbitmap); 
ReleaseDC(hwind, hdc); 
if (hdc2) { 
    DeleteDC(hdc2); 
} 

まず、私の知る限りでは、このコードでlineSizeSrcの値は、画面の解像度に依存するので、いくつかのゼロが追加されて常に正確ではありません各行はdataです。ゼロが追加されたときに、lineSizeSrcの正しい値を取得する方法を教えてください。

第2に、グラフィックカードを4K解像度で出力するなど、モニタの解像度に関係なく、4K解像度でキャプチャされた画像を取得できますか?

答えて

2

まず、私の知る限り、このコードでlineSizeSrcの値は必ずしも正しくない画面の解像度に依存するので、いくつかのゼロがデータの各ラインに追加することができます。ゼロが追加されたときに、lineSizeSrcの正しい値を取得する方法を教えてください。

ビットマップ形式では、各行が4バイトの倍数のアドレスで始まることが必要です。多くの場合、一般的な画像の幅は4の倍数であるため、または個々のピクセルのサイズが32ビット(4バイト)であるため、これはちょうどうまくいきます。

しかし、画像の幅が31ピクセル(幅が31ピクセル)で、1ピクセルあたり24ビット(3バイト)のような画像を表現する場合は、各行の最後を埋め込む必要があります。次の行は、これを行うための一般的な方法は、「ストライド」を切り上げることです4.

の倍数で開始:

lineSizeSrc = (resx * BitsPerPixel + 31)/8; 

resx * BitsPerPixelは私たちにラインを表現するのに必要なビット数を伝えます。 8で除算するとバイトがバイトに変換されます - sort of。整数部はすべての余りを切り捨てます。最初に31を追加することで、必要なビット数以上の32ビット(4バイト)の最小倍数が切り捨てられます。したがって、lineSizeSrcは、各行に必要なバイト数です。

resxの代わりにlineSizeSrcを使用して、必要なバイト数を計算する必要があります。

第2に、グラフィックカードが4K解像度で出力されるなど、モニタの解像度に関係なく、4K解像度でキャプチャされた画像を取得できますか?

シンプルなオールインケース方式はありません。あなたの最善の策はおそらく、グラフィックスカードがそのモードにない場合でも、4Kのウィンドウにレンダリングするようにプログラムに頼むことです。これをサポートするプログラムもあれば、今のプログラムもあります。 WM_PRINTWM_PRINTCLIENTのメッセージを参照してください。

2

ほとんどのモダンモニタは32ビットカラーをサポートしていますが、パレットを必要としないため比較的シンプルです。 C++での例:

void capture(char* &buffer) 
{ 
    HWND hwnd = GetDesktopWindow(); 
    HDC hdc = GetDC(hwnd); 
    int w = GetSystemMetrics(SM_CXSCREEN); 
    int h = GetSystemMetrics(SM_CYSCREEN); 
    int BitsPerPixel = GetDeviceCaps(hdc, BITSPIXEL); 
    if (BitsPerPixel = 32) 
    { 
     HDC memdc = CreateCompatibleDC(hdc); 
     HBITMAP bmp = CreateCompatibleBitmap(hdc, w, h); 
     HGDIOBJ oldbitmap = SelectObject(memdc, bmp); 
     BitBlt(memdc, 0, 0, w, h, hdc, 0, 0, CAPTUREBLT | SRCCOPY); 
     SelectObject(memdc, oldbitmap); 

     DWORD bitsize = w * h * 4; 
     char *bits = new char[bitsize]; 

     DWORD szInfoHdr = sizeof(BITMAPINFOHEADER); 
     BITMAPINFOHEADER bmpInfoHeader = 
     { szInfoHdr, w, h, 1, (WORD)BitsPerPixel, BI_RGB, 0, 0, 0, 0, 0 }; 

     GetDIBits(hdc, bmp, 0, h, bits, (BITMAPINFO*)&bmpInfoHeader, DIB_RGB_COLORS); 

     buffer = new char[bitsize + szInfoHdr]; 
     memcpy(buffer, &bmpInfoHeader, szInfoHdr); 
     memcpy(buffer + szInfoHdr, bits, bitsize); 
     delete[]bits; 
     DeleteObject(bmp); 
     DeleteObject(memdc); 
    } 

    ReleaseDC(hwnd, hdc); 
} 

あなたがスルー機能bufferを渡すことができます。次のコードは、試験のために使用することができる。

case WM_PAINT: 
{ 
    PAINTSTRUCT ps; 
    HDC hdc = BeginPaint(hWnd, &ps); 
    char *buffer = 0; 

    //capture the screen and save to buffer 
    capture(buffer); 

    if (buffer) 
    { 
     //paint the buffer for testing: 
     BITMAPINFO* bmpinfo = (BITMAPINFO*)buffer; 
     if (bmpinfo->bmiHeader.biBitCount == 32) 
     { 
      int w = bmpinfo->bmiHeader.biWidth; 
      int h = bmpinfo->bmiHeader.biHeight; 
      char *bits = buffer + sizeof(BITMAPINFOHEADER); 
      HBITMAP hbitmap = CreateDIBitmap(hdc, 
        &bmpinfo->bmiHeader, CBM_INIT, bits, bmpinfo, DIB_RGB_COLORS); 
      HDC memdc = CreateCompatibleDC(hdc); 
      SelectObject(memdc, hbitmap); 
      BitBlt(hdc, 0, 0, w, h, memdc, 0, 0, SRCCOPY); 
     } 
     delete[]buffer; 
    } 
    EndPaint(hWnd, &ps); 
} 

注ただし、GetSystemMetrics(SM_CXSCREEN)は、プライマリモニタの幅を返します。

マルチモニタの幅/高さは、SM_CXVIRTUALSCREENSM_CYVIRTUALSCREENとすることができます。左上にはSM_(X/Y)VIRTUALSCREENを使用してください。

関連する問題