Обрезать HBITMAP с C ++ на Windows - PullRequest
0 голосов
/ 25 февраля 2020

У меня есть HBITMAP, удерживающий скриншот окна. Теперь я хочу вырезать из него определенную область / прямоугольник и вернуть его как новый HBITMAP.

Однако следующий код только сжимает изображение до правильного нового размера прямоугольника, но не обрезает его:

HBITMAP crop_image(const RECT rectangle, const HBITMAP source_image)
{
    const auto h_clone = static_cast<HBITMAP>(CopyImage(source_image, IMAGE_BITMAP, rectangle.right - rectangle.left,
                                                        rectangle.bottom - rectangle.top, LR_CREATEDIBSECTION));

    const auto hdc_mem = CreateCompatibleDC(nullptr);
    const auto hdc_mem2 = CreateCompatibleDC(nullptr);

    const auto h_old_bmp = static_cast<HBITMAP>(SelectObject(hdc_mem, source_image));
    const auto h_old_bmp2 = static_cast<HBITMAP>(SelectObject(hdc_mem2, h_clone));

    BitBlt(hdc_mem2, 0, 0, rectangle.right - rectangle.left, rectangle.bottom - rectangle.top,
           hdc_mem, rectangle.left, rectangle.top, SRCCOPY);

    SelectObject(hdc_mem, h_old_bmp);
    SelectObject(hdc_mem2, h_old_bmp2);

    DeleteDC(hdc_mem);
    DeleteDC(hdc_mem2);

    return h_clone;
}

Как я могу исправить свой код, чтобы обрезать изображение по желанию?

Ответы [ 2 ]

2 голосов
/ 25 февраля 2020

Я не уверен, сработает ли это или нет, потому что я долгое время не работал над Win32, поэтому давайте попробуем.

Вот идея:

  1. Создание новой памяти D C.
  2. Создание нового растрового изображения вместо клонирования из источника.
  3. BitBlt целевой прямоугольник исходного растрового изображения в тот, который только что создан.

Создайте новый D C и растровое изображение

Используйте CreateCompatibleDC, как вы это сделали для создания памяти D C. Затем создайте новое растровое изображение с CreateCompatibleBitmap. Ширина и высота растрового изображения будет размером обрезанного растрового изображения. Затем выберите его в D C с помощью SelectObject.

Скопируйте целевой прямоугольник из источника

Теперь вам нужно создать новый D C для исходного растрового изображения с помощью CreateCompatibleDC и выберите исходное растровое изображение в нем с помощью SelectObject. Затем используйте BitBlt, чтобы скопировать прямоугольник исходного растрового изображения в растровое изображение, которое вы только что создали. Пункт назначения x и y будет нулевым, потому что мы хотим начать рисование с левого верхнего угла. Источник x и y - это место, с которого вы хотите начать копирование из исходного растрового изображения.

0 голосов
/ 26 февраля 2020

Ваш код может быть уменьшен до этого:

HBITMAP crop_image(const RECT rectangle, const HBITMAP source_image)
{
    const auto h_clone = static_cast<HBITMAP>(CopyImage(source_image, IMAGE_BITMAP, rectangle.right - rectangle.left,
                                                        rectangle.bottom - rectangle.top, LR_CREATEDIBSECTION));    

    return h_clone;
}

Удаленный код не делает ничего полезного. После тестирования я могу получить обрезанную область, а не уменьшенный скриншот. Поскольку вы не предоставили полный код, я не смог получить больше выводов.

Для процесса отсечения скриншотов мы можем легко изменить его в соответствии с официальным примером.

Захват изображения

Используемые API подробно описаны в примере.

Вам нужно только изменить часть кода, например, изменение области обрезки и пример также показано, как сохранить обрезанную область в виде файла.

int CaptureAnImage(HWND hWnd)
{
    HDC hdcScreen;
    HDC hdcWindow;
    HDC hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;
    RECT rc;
    rc.left = 0;
    rc.top = 0;
    rc.right = 800;
    rc.bottom = 600;

    // Retrieve the handle to a display device context for the client 
    // area of the window. 
    hdcScreen = GetDC(NULL);
    hdcWindow = GetDC(hWnd);

    // Create a compatible DC which is used in a BitBlt from the window DC
    hdcMemDC = CreateCompatibleDC(hdcWindow);

    if (!hdcMemDC)
    {
        MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
    }

    // Get the client area for size calculation
    RECT rcClient;
    GetClientRect(hWnd, &rcClient);

    //This is the best stretch mode
    SetStretchBltMode(hdcWindow, HALFTONE);

    //The source DC is the entire screen and the destination DC is the current window (HWND)
    if (!StretchBlt(hdcWindow,
        0, 0,
        rcClient.right, rcClient.bottom,
        hdcScreen,
        0, 0,
        GetSystemMetrics(SM_CXSCREEN),
        GetSystemMetrics(SM_CYSCREEN),
        SRCCOPY))
    {
        MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);
    }

    // Create a compatible bitmap from the Window DC
    hbmScreen = CreateCompatibleBitmap(hdcWindow, rc.right - rc.left, rc.bottom - rc.top);

    if (!hbmScreen)
    {
        MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
    }

    // Select the compatible bitmap into the compatible memory DC.
    SelectObject(hdcMemDC, hbmScreen);

    // Bit block transfer into our compatible memory DC.


    if (!BitBlt(hdcMemDC,
        rc.left, rc.top,
        rc.right - rc.left, rc.bottom - rc.top,
        hdcWindow,
        0, 0,
        SRCCOPY))
    {
        MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
    }



//  HBITMAP bmpnew = crop_image(rc, hbmScreen);

    // Get the BITMAP from the HBITMAP
    GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);

    BITMAPFILEHEADER   bmfHeader;
    BITMAPINFOHEADER   bi;

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bmpScreen.bmWidth;
    bi.biHeight = bmpScreen.bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight;

    // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
    // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
    // have greater overhead than HeapAlloc.
    HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
    char *lpbitmap = (char *)GlobalLock(hDIB);

    // Gets the "bits" from the bitmap and copies them into a buffer 
    // which is pointed to by lpbitmap.
    GetDIBits(hdcWindow, hbmScreen, 0,
        (UINT)bmpScreen.bmHeight,
        lpbitmap,
        (BITMAPINFO *)&bi, DIB_RGB_COLORS);

    // A file is created, this is where we will save the screen capture.
    HANDLE hFile = CreateFile(L"captureqwsx.bmp",
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, NULL);

    // Add the size of the headers to the size of the bitmap to get the total file size
    DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

    //Offset to where the actual bitmap bits start.
    bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER);

    //Size of the file
    bmfHeader.bfSize = dwSizeofDIB;

    //bfType must always be BM for Bitmaps
    bmfHeader.bfType = 0x4D42; //BM   

    DWORD dwBytesWritten = 0;
    WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

    //Unlock and Free the DIB from the heap
    GlobalUnlock(hDIB);
    GlobalFree(hDIB);

    //Close the handle for the file that was created
    CloseHandle(hFile);

    //Clean up
    DeleteObject(hbmScreen);
    DeleteObject(hdcMemDC);
    ReleaseDC(NULL, hdcScreen);
    ReleaseDC(hWnd, hdcWindow);

    return 0;
}

Будет получен файл обрезанного снимка экрана (800x600) текущего экрана.

...