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

C 处理GetDIBits()返回的像素缓冲区的正确方法是什么?,c,winapi,sdl,gdi,C,Winapi,Sdl,Gdi,我一直在尝试制作一个SDL程序,它能够拍摄整个屏幕的截图,在本例中,它可以显示整个监视器屏幕的实时提要。我已经成功地使用GDI函数检索了一个图像,但我不知道如何在GetDIBits()函数返回后正确处理缓冲区中的数据输出。到目前为止,我的图像与预期输出相差甚远。在我目前尝试的所有格式中,颜色都是乱七八糟的,这是SDL纹理可用的大多数32位和24位像素格式。我的win32代码也可能有错误,我不能完全确定,因为显示的图像不正确 以下是我如何获得屏幕截图: void WINAPI get_screen

我一直在尝试制作一个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(),但我认为您应该能够知道它的作用

生成的图像如下所示:


可以随意添加更好的方法或纯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);
就这么简单


valter

看起来潜在的问题是在哪里创建兼容的位图。位图的创建应与显示的精确大小一致,而不是SDL位图的大小。你永远不想让GDI缩小位图的比例,因为你会得到你所看到的结果。当你试图获得一个简单的屏幕截图时,为什么要使用sdl呢。仅使用winapi,您就可以用最少的编码实现相同的结果。@BitBank我更改了它,但它没有完全修复它,只是使图像稍微好一点。@Valter是的,我试图获得一个简单的屏幕截图,但在处理事件时,我还在GUI屏幕上循环将其更新到GPU内存中。虽然我以前做过纯win32 API GUI,但SDL API要简单得多,而且可能比我通常在此实例中创建的代码要快。它也可以很容易地重用或更改。我已经编写了代码来做同样的事情,我看到的区别是我使用CreateDIBSection()处理屏幕的DC,而不是内存DC。我还创建了一个带有BI_位字段的BitMapInfo标头,并将位字段设置为与显示的位深度匹配。DIBSection中的bitblt将立即将像素数据作为指针提供给您。