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

Posted

技术标签:

【中文标题】处理 GetDIBits() 返回的像素缓冲区的正确方法是啥?【英文标题】:What is the proper way to handle the pixel buffer returned by GetDIBits()?处理 GetDIBits() 返回的像素缓冲区的正确方法是什么? 【发布时间】:2014-03-05 13:52:57 【问题描述】:

我一直在尝试制作一个 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(),它返回正确的值。但是,多次调用的开销很大。

【问题讨论】:

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

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

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

就这么简单。

变态

【讨论】:

以上是关于处理 GetDIBits() 返回的像素缓冲区的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

GetDIBits返回兼容位图的无效颜色数组

GetDIBits 遍历位图 获取像素的颜色值(RGB)

GetDIBits 遍历位图 获取像素的颜色值(RGB)

GetDIBits 返回所有值为 0 的数组

WebGL 帧缓冲区 ClearColor 仅影响 (0,0) 像素

VC++将位图中保存的图像灰化(附源码)