gpt4 book ai didi

windows - 将 Windows 屏幕截图捕获位图渲染为 DirectX 纹理

转载 作者:可可西里 更新时间:2023-11-01 13:44:20 41 4
gpt4 key购买 nike

我正在开发“3d 桌面”directx 应用程序,该应用程序需要在 directx (11) 的矩形表面上将桌面窗口(例如“计算器”)的当前内容显示为二维纹理。我太接近了,但真的很难处理屏幕截图 BMP -> Texture2D 步骤。我确实有屏幕截图->HBITMAP 和 DDSFile->渲染纹理成功工作但无法完成屏幕截图->渲染纹理。

到目前为止,我已经完成了“将窗口捕获为屏幕截图”的工作:

RECT user_window_rectangle;
HWND user_window = FindWindow(NULL, TEXT("Calculator"));
GetClientRect(user_window, &user_window_rectangle);
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
UINT screenshot_width = user_window_rectangle.right - user_window_rectangle.left;
UINT screenshot_height = user_window_rectangle.bottom - user_window_rectangle.top;
hbmp = CreateCompatibleBitmap(hdcScreen, screenshot_width, screenshot_height);
SelectObject(hdc, hbmp);
PrintWindow(user_window, hdc, PW_CLIENTONLY);

此时我有了 HBITMAP hbmp 引用的窗口位图。

我的代码也可以将 DDS 文件渲染为 directx/3d 矩形上的纹理:

ID3D11Device *dev;
ID3D11DeviceContext *dev_context;
...
dev_context->PSSetShaderResources(0, 1, &shader_resource_view);
dev_context->PSSetSamplers(0, 1, &tex_sampler_state);
...
DirectX::TexMetadata tex_metadata;
DirectX::ScratchImage image;

hr = LoadFromDDSFile(L"Earth.dds", DirectX::DDS_FLAGS_NONE, &tex_metadata, image);
hr = CreateShaderResourceView(dev, image.GetImages(), image.GetImageCount(), tex_metadata, &shader_resource_view);

像素着色器是:

Texture2D ObjTexture
SamplerState ObjSamplerState
float4 PShader(float4 pos : SV_POSITION, float4 color : COLOR, float2 tex : TEXCOORD) : SV_TARGET\
{
return ObjTexture.Sample( ObjSamplerState, tex );
}

采样器状态(默认为线性)是:

D3D11_SAMPLER_DESC sampler_desc;
ZeroMemory(&sampler_desc, sizeof(sampler_desc));
sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampler_desc.MinLOD = 0;
sampler_desc.MaxLOD = D3D11_FLOAT32_MAX;

hr = dev->CreateSamplerState(&sampler_desc, &tex_sampler_state);

问题:如何将 LoadFromDDSFile 位替换为从 Windows 屏幕捕获中获取 HBITMAP 并最终在显卡上作为 ObjTexture 的等效项?

下面是我从截图 HBITMAP hbmp 桥接到着色器资源 screenshot_texture 的最佳镜头,但它给出了来自图形驱动程序的内存访问冲突(我认为是由于我的“data.pSysmem = &bmp.bmBits”,但没有想法真的):

GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bmp)

D3D11_TEXTURE2D_DESC screenshot_desc = CD3D11_TEXTURE2D_DESC(DXGI_FORMAT_R8G8B8A8_UNORM, bmp.bmWidth, bmp.bmHeight, 1,
1,
D3D11_BIND_SHADER_RESOURCE
);

int bytes_per_pixel = 4;

D3D11_SUBRESOURCE_DATA data;
ZeroMemory(&data, sizeof(D3D11_SUBRESOURCE_DATA));
data.pSysMem = &bmp.bmBits; //pixel buffer
data.SysMemPitch = bytes_per_pixel * bmp.bmWidth;// line size in byte
data.SysMemSlicePitch = bytes_per_pixel * bmp.bmWidth * bmp.bmHeight;// total buffer size in byte

hr = dev->CreateTexture2D(
&screenshot_desc, //texture format
&data, // pixel buffer use to fill the texture
&screenshot_texture // created texture
);

::::::::::::::::::::::::::::解决方案::::::::::::::::::::::::::::::::::::::::::

主要问题是尝试将 &bmp.bmBits 直接用作像素缓冲区导致图形驱动程序内的内存冲突 - 通过使用“malloc”分配适当大小的内存块来存储像素数据解决了这个问题。感谢 Chuck Walbourn 帮助我在黑暗中四处寻找像素数据的实际存储方式(默认情况下实际上是 32 位/像素)。仍然有可能/可能某些代码依赖于运气来正确读取像素数据,但 Chuck 的输入已对其进行了改进。

我的基本技术是;

  • FindWindow 获取桌面上的客户端窗口
  • CreateCompatibleBitmapSelectObjectPrintWindow 获取快照的 HBITMAP
  • ma​​lloc 为(字节*)像素缓冲区分配正确的空间量
  • GetDIBits 从 HBITMAP 填充(字节*)像素缓冲区
  • CreateTexture2D 构建纹理缓冲区
  • CreateShaderResourceView 将纹理映射到图形像素着色器

因此,截取 Windows 桌面窗口并将其作为纹理传递给 direct3d 应用程序的工作代码是:

RECT user_window_rectangle;

HWND user_window = FindWindow(NULL, TEXT("Calculator")); //the window can't be min
if (user_window == NULL)
{
MessageBoxA(NULL, "Can't find Calculator", "Camvas", MB_OK);
return;
}
GetClientRect(user_window, &user_window_rectangle);
//create
HDC hdcScreen = GetDC(NULL);
HDC hdc = CreateCompatibleDC(hdcScreen);
UINT screenshot_width = user_window_rectangle.right - user_window_rectangle.left;
UINT screenshot_height = user_window_rectangle.bottom - user_window_rectangle.top;
hbmp = CreateCompatibleBitmap(hdcScreen, screenshot_width, screenshot_height);

SelectObject(hdc, hbmp);

//Print to memory hdc
PrintWindow(user_window, hdc, PW_CLIENTONLY);

BITMAPINFOHEADER bmih;
ZeroMemory(&bmih, sizeof(BITMAPINFOHEADER));
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biPlanes = 1;
bmih.biBitCount = 32;
bmih.biWidth = screenshot_width;
bmih.biHeight = 0-screenshot_height;
bmih.biCompression = BI_RGB;
bmih.biSizeImage = 0;

int bytes_per_pixel = bmih.biBitCount / 8;

BYTE *pixels = (BYTE*)malloc(bytes_per_pixel * screenshot_width * screenshot_height);

BITMAPINFO bmi = { 0 };
bmi.bmiHeader = bmih;

int row_count = GetDIBits(hdc, hbmp, 0, screenshot_height, pixels, &bmi, DIB_RGB_COLORS);

D3D11_TEXTURE2D_DESC screenshot_desc = CD3D11_TEXTURE2D_DESC(
DXGI_FORMAT_B8G8R8A8_UNORM, // format
screenshot_width, // width
screenshot_height, // height
1, // arraySize
1, // mipLevels
D3D11_BIND_SHADER_RESOURCE, // bindFlags
D3D11_USAGE_DYNAMIC, // usage
D3D11_CPU_ACCESS_WRITE, // cpuaccessFlags
1, // sampleCount
0, // sampleQuality
0 // miscFlags
);

D3D11_SUBRESOURCE_DATA data;
ZeroMemory(&data, sizeof(D3D11_SUBRESOURCE_DATA));
data.pSysMem = pixels; // texArray; // &bmp.bmBits; //pixel buffer
data.SysMemPitch = bytes_per_pixel * screenshot_width;// line size in byte
data.SysMemSlicePitch = bytes_per_pixel * screenshot_width * screenshot_height;

hr = dev->CreateTexture2D(
&screenshot_desc, //texture format
&data, // pixel buffer use to fill the texture
&screenshot_texture // created texture
);

D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
srvDesc.Format = screenshot_desc.Format;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.MostDetailedMip = screenshot_desc.MipLevels;

dev->CreateShaderResourceView(screenshot_texture, NULL, &shader_resource_view);

最佳答案

您在这里做了很多假设,即返回的 BITMAP 实际上是 32 位 RGBA 格式。它可能根本不是那种格式,并且在任何情况下,如果您假设它是每个像素 4 个字节。您应该阅读有关 BMP 的更多信息格式。

BMP 使用 BGRA 顺序,因此对于 bmBitsPixel 为 32 的情况,您可以使用 DXGI_FORMAT_B8G8R8A8_UNORM

其次,您需要从 bmWidthBytes 而不是 bmWidth 中导出间距。

data.pSysMem = &bmp.bmBits; //pixel buffer
data.SysMemPitch = bmp.bmWidthBytes;// line size in byte
data.SysMemSlicePitch = bmp.bmWidthBytes * bmp.bmHeight;// total buffer size in byte

如果 bmBitsPixel 是 24,则没有与之等效的 DXGI 格式。您必须将数据复制为 32 位格式,例如 DXGI_FORMAT_B8G8R8X8_UNORM

如果 bmBitsPixel 为 15 或 16,您可以在具有 Direct3D 11.1 的系统上使用 DXGI_FORMAT_B5G5R5A1_UNORM,但请记住,并不总是支持 16 位 DXGI 格式,具体取决于司机。否则,您必须将此数据转换为其他数据。

对于 1、2、4 或 8 的 bmBitsPixel 值,您必须转换它们,因为没有等效的 DXGI 纹理格式。

关于windows - 将 Windows 屏幕截图捕获位图渲染为 DirectX 纹理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27150901/

41 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com