以编程方式拍摄屏幕截图并保存为PNG

时间:2017-02-25 12:41:27

标签: winapi

我一直在使用以下代码:

#include <fstream>
#include <gdiplus.h>
#include <windows.h>

#include <iostream>

using namespace std;

void CaptureScreen(const char* filename)
{
    HDC hScreenDC = GetDC(0);
    HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

    int upper_left_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
    int upper_left_y = GetSystemMetrics(SM_YVIRTUALSCREEN);

    int bitmap_dx = GetSystemMetrics(SM_CXVIRTUALSCREEN ) * 1.25f;
    int bitmap_dy = GetSystemMetrics(SM_CYVIRTUALSCREEN ) * 1.25f;

    // create file
    ofstream file(filename, ios::binary);
    if(!file) return;

    // save bitmap file headers
    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER infoHeader;

    fileHeader.bfType      = 0x4d42;
    fileHeader.bfSize      = 0;
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    infoHeader.biSize          = sizeof(infoHeader);
    infoHeader.biWidth         = bitmap_dx;
    infoHeader.biHeight        = -bitmap_dy;
    infoHeader.biPlanes        = 1;
    infoHeader.biBitCount      = 16;
    infoHeader.biCompression   = BI_RGB;
    infoHeader.biSizeImage     = 0;
    infoHeader.biXPelsPerMeter = 0;
    infoHeader.biYPelsPerMeter = 0;
    infoHeader.biClrUsed       = 0;
    infoHeader.biClrImportant  = 0;

    file.write((char*)&fileHeader, sizeof(fileHeader));
    file.write((char*)&infoHeader, sizeof(infoHeader));

    // dibsection information
    BITMAPINFO info;
    info.bmiHeader = infoHeader;

    // ------------------
    // THE IMPORTANT CODE
    // ------------------
    // create a dibsection and blit the window contents to the bitmap

    BYTE* memory = 0;
    HBITMAP bitmap = CreateDIBSection(hScreenDC, &info, DIB_RGB_COLORS, (void**)&memory, 0, 0);
    SelectObject(hMemoryDC, bitmap);
    BitBlt(hMemoryDC, 0, 0, bitmap_dx, bitmap_dy, hScreenDC, upper_left_x, upper_left_y, SRCCOPY);
    DeleteDC(hMemoryDC);
    ReleaseDC(NULL, hScreenDC);

    // save dibsection data
    int bytes = (((16*bitmap_dx + 31) & (~31))/8)*bitmap_dy;
    file.write((const char *)memory, bytes);

    DeleteObject(bitmap);
}

int main()
{
    CaptureScreen("ok.jpg");

    return 0;
}

但它似乎生成了一个太大的BMP文件,因为位图是未压缩保存的。

我正在寻找一种方法来捕获屏幕截图并将其保存为PNG格式的缓冲区,通过TCP连接发送并将其保存为PNG文件。

我认为这与将BI_PNG分配给infoHeader.biCompression以及bytes的不同计算有关,但我无法确切地知道是什么。

1 个答案:

答案 0 :(得分:0)

http://lodev.org/lodepng/

#include <fstream>
#include <gdiplus.h>
#include <windows.h>

#include <iostream>

#include "lodepng.h"

const int bits_per_pixel = 24;


using namespace std;

void CaptureScreen(const char* filename)
{
    HDC hScreenDC = GetDC(0);
    HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

    int upper_left_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
    int upper_left_y = GetSystemMetrics(SM_YVIRTUALSCREEN);

    int bitmap_dx = GetSystemMetrics(SM_CXVIRTUALSCREEN ) * 1.25f;
    int bitmap_dy = GetSystemMetrics(SM_CYVIRTUALSCREEN ) * 1.25f;

    // create file
    ofstream file(filename, ios::binary);
    if(!file) return;

    // save bitmap file headers
    BITMAPFILEHEADER fileHeader;
    BITMAPINFOHEADER infoHeader;

    fileHeader.bfType      = 0x4d42;
    fileHeader.bfSize      = 0;
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfOffBits   = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    infoHeader.biSize          = sizeof(infoHeader);
    infoHeader.biWidth         = bitmap_dx;
    infoHeader.biHeight        = -bitmap_dy;
    infoHeader.biPlanes        = 1;
    infoHeader.biBitCount      = bits_per_pixel;
    infoHeader.biCompression   = BI_RGB;
    infoHeader.biSizeImage     = 0;
    infoHeader.biXPelsPerMeter = 0;
    infoHeader.biYPelsPerMeter = 0;
    infoHeader.biClrUsed       = 0;
    infoHeader.biClrImportant  = 0;

    file.write((char*)&fileHeader, sizeof(fileHeader));
    file.write((char*)&infoHeader, sizeof(infoHeader));

    // dibsection information
    BITMAPINFO info;
    info.bmiHeader = infoHeader; 

    // ------------------
    // THE IMPORTANT CODE
    // ------------------
    // create a dibsection and blit the window contents to the bitmap

    BYTE* memory = 0;
    HBITMAP bitmap = CreateDIBSection(hScreenDC, &info, DIB_RGB_COLORS, (void**)&memory, 0, 0);
    SelectObject(hMemoryDC, bitmap);
    BitBlt(hMemoryDC, 0, 0, bitmap_dx, bitmap_dy, hScreenDC, upper_left_x, upper_left_y, SRCCOPY);
    DeleteDC(hMemoryDC);
    ReleaseDC(NULL, hScreenDC);

    // save dibsection data
    int bytes = (((bits_per_pixel*bitmap_dx + 31) & (~31))/8)*bitmap_dy;
    file.write((const char *)memory, bytes);

    unsigned char *out_buffer;
    size_t out_buffer_len;

    unsigned error;
    if ( bits_per_pixel == 24 )
    {
        // convert memory from bgr format to rgb
        for ( unsigned i = 0; i< bytes-2; i+=3)
        {
            int tmp = memory[i+2];
            memory[i+2] = memory[i];
            memory[i] = tmp;
        }

        error = lodepng_encode24(&out_buffer,
                                          &out_buffer_len,
                                          memory,
                                          bitmap_dx,
                                          bitmap_dy);
    }

    if ( bits_per_pixel == 32 )
    {
        // convert memory from bgr format to rgb
        for ( unsigned i = 0; i< bytes-3; i+=4)
        {
            int tmp = memory[i+2];
            memory[i+2] = memory[i];
            memory[i] = tmp;
        }

        error = lodepng_encode32(&out_buffer,
                                          &out_buffer_len,
                                          memory,
                                          bitmap_dx,
                                          bitmap_dy);
    }


    if ( error )
    {
        std::cout << "error: " << error << '\n';
        return;
    }

    lodepng_save_file(out_buffer, out_buffer_len, "stam.png");




    // free(out);
    DeleteObject(bitmap);
}

int main()
{
    CaptureScreen("ok.jpg");

    return 0;
}