Как создать растровое изображение из Surface (SharpDX)

Я новичок в DirectX и пытаюсь использовать SharpDX, чтобы сделать снимок экрана, используя Desktop Duplication 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)?

2 ответа

Решение

Страница MSDN для API Desktop Duplication сообщает нам формат изображения:

DXGI предоставляет поверхность, которая содержит текущее изображение рабочего стола с помощью нового метода IDXGIOutputDuplication::AcquireNextFrame. Формат изображения рабочего стола всегда DXGI_FORMAT_B8G8R8A8_UNORM независимо от текущего режима отображения.

Вы можете использовать Surface.Map(MapFlags, out DataStream) Метод получения доступа к данным на процессоре.

Код должен выглядеть примерно так:

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 под рукой, я только следую документации. Я надеюсь, что это работает:)

Я только что закончил это сам, хотя я не собираюсь много говорить об этом коде!

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, но если кто-то застрянет, я надеюсь, что это поможет!

Другие вопросы по тегам