【问题标题】:How to create bitmap from Surface (SharpDX)如何从 Surface (SharpDX) 创建位图
【发布时间】:2013-04-15 17:16:21
【问题描述】:

我是 DirectX 的新手,并尝试使用 SharpDX 通过桌面复制 API 捕获屏幕截图。

我想知道是否有任何简单的方法可以创建可以在 CPU 中使用的位图(即保存在文件中等)

我正在使用以下代码获取桌面屏幕截图:

var factory = new SharpDX.DXGI.Factory1();
var adapter = factory.Adapters1[0];
var output = adapter.Outputs[0];

var device = new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware,
                                                       DeviceCreationFlags.BgraSupport |
                                                       DeviceCreationFlags.Debug);

var dev1 = device.QueryInterface<SharpDX.DXGI.Device1>();

var output1 = output.QueryInterface<Output1>();
var duplication = output1.DuplicateOutput(dev1);
OutputDuplicateFrameInformation frameInfo;
SharpDX.DXGI.Resource desktopResource;
duplication.AcquireNextFrame(50, out frameInfo, out desktopResource);

var desktopSurface = desktopResource.QueryInterface<Surface>();

谁能告诉我如何从 desktopSurface(DXGI.Surface 实例)创建位图对象?

【问题讨论】:

    标签: directx-11 sharpdx


    【解决方案1】:

    我自己刚刚完成了这个,虽然我不会对这段代码多说!

    public byte[] GetScreenData()
        {
            // We want to copy the texture from the back buffer so 
            // we don't hog it.
            Texture2DDescription desc = BackBuffer.Description;
            desc.CpuAccessFlags = CpuAccessFlags.Read;
            desc.Usage = ResourceUsage.Staging;
            desc.OptionFlags = ResourceOptionFlags.None;
            desc.BindFlags = BindFlags.None;
    
            byte[] data = null;
    
            using (var texture = new Texture2D(DeviceDirect3D, desc))
            {
                DeviceContextDirect3D.CopyResource(BackBuffer, texture);
    
                using (Surface surface = texture.QueryInterface<Surface>())
                {
                    DataStream dataStream;
                    var map = surface.Map(SharpDX.DXGI.MapFlags.Read, out dataStream);
                    int lines = (int)(dataStream.Length / map.Pitch);
                    data = new byte[surface.Description.Width * surface.Description.Height * 4];
    
                    int dataCounter = 0;
                    // width of the surface - 4 bytes per pixel.
                    int actualWidth = surface.Description.Width * 4;
                    for (int y = 0; y < lines; y++)
                    {
                        for (int x = 0; x < map.Pitch; x++)
                        {
                            if (x < actualWidth)
                            {
                                data[dataCounter++] = dataStream.Read<byte>();
                            }
                            else
                            {
                                dataStream.Read<byte>();
                            }
                        }
                    }
                    dataStream.Dispose();
                    surface.Unmap();
                }
            }
    
            return data;
        }
    

    这会给你一个字节[],然后可以用来生成位图。

    以下是我如何保存到 png 图像。

     using (var stream = await file.OpenAsync( Windows.Storage.FileAccessMode.ReadWrite ))
                {
                    BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
                    double dpi = DisplayProperties.LogicalDpi;
    
                    encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight,
                        (uint)width, (uint)height, dpi, dpi, pixelData);
                    encoder.BitmapTransform.ScaledWidth = (uint)newWidth;
                    encoder.BitmapTransform.ScaledHeight = (uint)newHeight;
                    await encoder.FlushAsync();
                    waiter.Set();
                }
    

    我知道这已在不久前得到回答,也许您现在已经想通了:3 但如果其他人遇到困难,我希望这会有所帮助!

    【讨论】:

    • 谢谢老兄 :D 上帝我喜欢这个网站
    • 嗨,你的解决方案在我的情况下不起作用,我知道这篇文章晚了几年,但我打开了一个引用你的代码的新问题。如果你能帮助我,这里是链接:stackoverflow.com/questions/44908867/…
    【解决方案2】:

    MSDN page for the Desktop Duplication API 告诉我们图片的格式:

    DXGI 通过新的 IDXGIOutputDuplication::AcquireNextFrame 方法提供包含当前桌面图像的表面。无论当前显示模式是什么,桌面图像的格式始终是DXGI_FORMAT_B8G8R8A8_UNORM

    您可以使用Surface.Map(MapFlags, out DataStream) 方法访问CPU 上的数据。

    代码应如下所示:

    DataStream dataStream;
    desktopSurface.Map(MapFlags.Read, out dataStream);
    for(int y = 0; y < surface.Description.Width; y++) {
        for(int x = 0; x < surface.Description.Height; x++) {
            // read DXGI_FORMAT_B8G8R8A8_UNORM pixel:
            byte b = dataStream.Read<byte>();
            byte g = dataStream.Read<byte>();
            byte r = dataStream.Read<byte>();
            byte a = dataStream.Read<byte>();
            // color (r, g, b, a) and pixel position (x, y) are available
            // TODO: write to bitmap or process otherwise
        }
    }
    desktopSurface.Unmap();
    

    *免责声明:我手头没有 Windows 8 安装,我只是按照文档进行操作。我希望这有效:)

    【讨论】:

    • 谢谢詹斯。但是,desktopSurface.Map(...) 函数会抛出以下异常:HRESULT: [0x80070057], Module: [Unknown], ApiCode: [Unknown/Unknown], Message: The parameter is incorrect. I am using win8.
    • @Shahed 当我为设备打开DeviceCreationFlags.Debug 时,我收到IDXGISurface::Map: This object was not created with CPUAccess flags that allow CPU access. 的消息。看来您必须首先使用 CPUAccess 将重复的表面复制到中间纹理,然后才能从 CPU 读取它(请参阅 Dracanus 答案)
    猜你喜欢
    • 1970-01-01
    • 2021-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多