c - 处理 GetDIBits() 返回的像素缓冲区的正确方法是什么?

标签 c winapi sdl gdi

我一直在尝试制作一个 SDL 程序,该程序能够截取整个屏幕的屏幕截图,在本例中,可以显示整个显示器屏幕的实时反馈。我已经成功地使用 GDI 函数检索图像,但我不知道在 GetDIBits() 函数返回后如何正确处理缓冲区中的数据输出。到目前为止,我的图像与预期输出相差甚远。我目前尝试过的所有格式的颜色都困惑不堪,其中大多数可用于 SDL 纹理的 32 位和 24 位像素格式。我的 win32 代码也可能有一个错误,我不完全确定,因为显示的图像不正确。

以下是我获取屏幕截图的方法:

void WINAPI get_screenshot( app_data * app )
{
    HDC desktop = GetDC( NULL );
    int width = GetDeviceCaps( desktop, HORZRES );
    int height = GetDeviceCaps( desktop, VERTRES );
    HDC desktop_copy = CreateCompatibleDC( 0 );

    HGDIOBJ old = NULL;
    HBITMAP screenshot = CreateCompatibleBitmap( desktop_copy, app->viewport.w, app->viewport.h );

    BITMAPINFOHEADER screenshot_header = { 0 };
    screenshot_header.biSize = sizeof( BITMAPINFOHEADER );
    screenshot_header.biWidth = app->viewport.w;
    screenshot_header.biHeight = -app->viewport.h;
    screenshot_header.biPlanes = 1;
    screenshot_header.biBitCount = 32;
    screenshot_header.biCompression = BI_RGB;


    if ( !screenshot )
    {
        ReleaseDC( NULL, desktop );
        DeleteDC( desktop_copy );
        DeleteObject( screenshot );
        free_app_data( app );
        win_error( "Creating Bitmap", true );
    }

    SetStretchBltMode( desktop_copy, HALFTONE );
    SetBrushOrgEx( desktop_copy, 0, 0, NULL );
    old = SelectObject( desktop_copy, screenshot );

    if ( !StretchBlt( desktop_copy, 0, 0, app->viewport.w, app->viewport.h, desktop, 0, 0, width, height, SRCCOPY ) )
    {
        ReleaseDC( NULL, desktop );
        DeleteDC( desktop_copy );
        DeleteObject( screenshot );
        free_app_data( app );
        win_error( "Stretching Screenshot to Window Size", true );
    }

    if ( !GetDIBits( desktop_copy, screenshot, 0, app->viewport.h, app->pixels, ( BITMAPINFO * )&screenshot_header, DIB_RGB_COLORS ) )
    {
        ReleaseDC( NULL, desktop );
        DeleteDC( desktop_copy );
        DeleteObject( screenshot );
        free_app_data( app );
        win_error( "Getting Window RGB Values", true );
    }

    SelectObject( desktop_copy, old );

    DeleteObject( screenshot );
    ReleaseDC( NULL, desktop );
    DeleteDC( desktop_copy );

    return;
}

我觉得调用我的 DLL 函数的大多数代码都是不言自明的,或者对于本文来说并不重要,但如果需要,我很乐意提供伪代码或纯 win32 API 代码。

创建 SDL 纹理和缓冲区的代码是:

    app->frame = SDL_CreateTexture(
                                   app->renderer,
                                   SDL_PIXELFORMAT_ABGR8888,
                                   SDL_TEXTUREACCESS_STREAMING,
                                   app->viewport.w,
                                   app->viewport.h
                                  );

    if ( !app->frame )
    {
        free_app_data( app );
        SDL_errorexit( "Creating texture", 1, TRUE );
    }

    app->pixels = ( Uint32 * )create_array( NULL, ( app->viewport.w * app->viewport.h ), sizeof( Uint32 ), zero_array );

    if ( !app->pixels )
    {
        free_app_data( app );
        std_error( "Creating pixel buffer", TRUE );
    }

同样,在本例中我使用了 DLL 函数 create_array(),但我认为您应该能够知道它的作用。

生成的图像是这样的:

The image output from my program 请随意添加更好的方法或纯 SDL 方法来执行此操作。我尝试过 GetPixel(),它返回正确的值。但多次调用的开销很高。

最佳答案

用于捕获屏幕并将其绘制到表单的代码:

HDC hdcScreen, hdcForm;

hdcScreen = GetDC(NULL);
hdcForm = GetWindowDC(hwnd); //hwnd is form handle

StretchBlt(hdcForm, 0, 0, formW, formH, hdcScreen , 0, 0, screenW, screenH, SRCCOPY );

ReleaseDC(hwnd, hdcForm);
ReleaseDC(NULL, hdcScreen);

就这么简单。

瓦尔特

关于c - 处理 GetDIBits() 返回的像素缓冲区的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21639837/

相关文章:

c++ - 如何获取窗口标题按钮的大小和位置(最小化、恢复、关闭)

c++ - 简单地包含 SDL header 会导致链接器错误

c++ - 使用 SDL 旋转图像的最佳方法?

c++ - C 和 C++ 预处理器之间有什么区别?

c - 为什么鼓励使用 "declared data types"而不鼓励使用 "processor-dependent data types"?

c++ - 可移植分支预测提示

c - 滚动条初始范围

c - 在Windows和除Linux之外的其他Unice中实现mmap的 `MAP_POPULATE`标志,以及在Windows中实现MADV_WILLNEED

c++ - SDL 加载我的图像搞砸了

c - 嵌套赋值语句和副作用