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

时间:2014-02-07 23:49:36

标签: 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(),它返回正确的值。尽管如此,它的开销很高。

1 个答案:

答案 0 :(得分:0)

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

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

就这么简单。

瓦尔特