处理 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() 返回的像素缓冲区的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章