Convert SlimDX.Direct3D11 Texture2D to .Net Bitmap

走远了吗. 提交于 2019-12-11 13:19:59

问题


Converting an .Net Bitmap to a SlimDx Texture2D works very fast like this: http://www.rolandk.de/index.php?option=com_content&view=article&id=65:bitmap-from-texture-d3d11&catid=16:blog&Itemid=10

private Texture2D TextureFromBitmap(FastBitmapSingle fastBitmap)
{
    Texture2D result = null;
    DataStream dataStream = new DataStream(fastBitmap.BitmapData.Scan0, fastBitmap.BitmapData.Stride * fastBitmap.BitmapData.Height, true, false);
    DataRectangle dataRectangle = new DataRectangle(fastBitmap.BitmapData.Stride, dataStream);
    try
    {
        Texture2DDescription dt = new Texture2DDescription
        {
            BindFlags = BindFlags.ShaderResource,
            CpuAccessFlags = CpuAccessFlags.None,
            Format = Format.B8G8R8A8_UNorm,
            OptionFlags = ResourceOptionFlags.None,
            MipLevels = 1,
            Usage = ResourceUsage.Immutable,
            Width = fastBitmap.Size.X,
            Height = fastBitmap.Size.Y,
            ArraySize = 1,
            SampleDescription = new SampleDescription(1, 0),
        };
        result = new Texture2D(device, dt, dataRectangle);
    }
    finally
    {
        dataStream.Dispose();
    }
    return result;
}

For converting the Texture back to a .Net Bitmap in the correct format I use that, but it is very slow:

private bool BitmapFromTexture(FastBitmapSingle fastBitmap, Texture2D texture)
{
    using (MemoryStream ms = new MemoryStream())
    {
        Texture2D.ToStream(device.ImmediateContext, texture, ImageFileFormat.Bmp, ms);
        ms.Position = 0;
        using (Bitmap temp1 = (Bitmap)Bitmap.FromStream(ms))
        {
            Rectangle bounds = new Rectangle(0, 0, temp1.Width, temp1.Height);
            BitmapData BitmapDataIn = temp1.LockBits(bounds, ImageLockMode.ReadWrite, temp1.PixelFormat);
            using (DataStream dataStreamIn = new DataStream(BitmapDataIn.Scan0, BitmapDataIn.Stride * BitmapDataIn.Height, true, false))
            using (DataStream dataStreamOut = new DataStream(fastBitmap.BitmapData.Scan0, fastBitmap.BitmapData.Stride * fastBitmap.BitmapData.Height, false, true))
            {
                dataStreamIn.CopyTo(dataStreamOut);
            }
            temp1.UnlockBits(BitmapDataIn);
            BitmapDataIn = null;
        }
    }
    return true;
}

Is there a faster way ??? I tried much, such as this:

But the DataRectangle has exactly 8 times more data then I need in my DataStream:

private bool BitmapFromTexture(FastBitmapSingle fastBitmap, Texture2D texture)
{
    using (Texture2D buff = Helper.CreateTexture2D(device, texture.Description.Width, texture.Description.Height, Format.B8G8R8A8_UNorm, BindFlags.None, ResourceUsage.Staging, CpuAccessFlags.Read | CpuAccessFlags.Write))
    {
        device.ImmediateContext.CopyResource(texture, buff);

        using (Surface surface = buff.AsSurface())
        using (DataStream dataStream = new DataStream(fastBitmap.BitmapData.Scan0, fastBitmap.BitmapData.Stride * fastBitmap.BitmapData.Height, false, true))
        {
            DataRectangle rect = surface.Map(SlimDX.DXGI.MapFlags.Read);

            rect.Data.CopyTo(dataStream);

            surface.Unmap();
        }
    }

    return true;
}

Can anybody help please? Copying back my data takes about 50% of the whole computation time. If this could be solved, my App would be much faster...


回答1:


The converter I'm using is this:

    public static BitmapSource Texture2DToBitmapSource(Texture2D texture2D)
    {
        using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
        {
            Texture2D.ToStream(App.device.ImmediateContext, texture2D, ImageFileFormat.Png, memoryStream);
            memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
            return BitmapFrame.Create(memoryStream, BitmapCreateOptions.IgnoreImageCache, BitmapCacheOption.OnLoad);
        }
    }

However I'm having a problem with the dpi being 72 and not 96 like the BitmapSource I'm using in my program. Hope it helps some.

Also i'm using SlimDX.

The WPF bitmap conversions are pretty much a headache from it's introduction. Getting maximum speed by going into unsafe code and lockbits is probably going to require conversions from Pbgra and bgra to rgba, etc. I usually ended up going back to the old Drawing Bitmap for converting to a BitmapSource in many programs. This sniplet is what i'm using untill i can get the correct PixelFormat to and from my compute shaders.

        using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
        {
            Texture2D.ToStream(App.device.ImmediateContext, texture, ImageFileFormat.Png, memoryStream);
            memoryStream.Seek(0, System.IO.SeekOrigin.Begin);

            //Create an image from a stream.
            System.Drawing.Image bitmap = System.Drawing.Bitmap.FromStream(memoryStream); memoryStream.Close();
            var hBitmap = ((System.Drawing.Bitmap)bitmap).GetHbitmap();
            BitmapSource source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                hBitmap,
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions()
                );
            if (!(source.Width == pixelWidth)) { }
            if (!(source.Height == pixelHeight)) { }
            return source;
        }

using the HBITMAP from a System.Drawing.Image or System.drawing.Bitmap has more control over lockbits and conversions for the different PixelFormats. The older CreateBitmap functions and DIB's use the HBITMAP for conversions also.

Another bottleneck in speed I would imagine being in the Texture2D.ToStream() routine. These ImageFileFormat.Png, ImageFileFormat.Bmp, ImageFileFormat.Jpg etc. SlimDX did not add some Copy function in the Texture2D, and DX11 has that somewhere.




回答2:


I found a solution thanks to: http://www.rolandk.de/wp/2013/06/inhalt-der-rendertarget-textur-in-ein-bitmap-kopieren/

But the story is a bit more complicated, because the Texture Pitch doesn't match the Bitmap Stride, so here my solution, 10 times faster than the one in my question:

private bool BitmapFromTexture(FastBitmapSingle fastBitmap, Texture2D texture, int row, int col)
{
    using (Texture2D stage = Helper.CreateStagingTexture(device, fastBitmap.BitmapWidths[col], fastBitmap.BitmapHeights[row]))
    {
        device.ImmediateContext.CopyResource(texture, stage);
        DataStream dsIn;
        DataBox dataBox = device.ImmediateContext.MapSubresource(stage, 0, 0, MapMode.Read, D3D.MapFlags.None, out dsIn);
        int dx = dataBox.RowPitch - fastBitmap.BitmapData[row][col].Stride;
        try
        {
            using (DataStream dsOut = new DataStream(fastBitmap.BitmapData[row][col].Scan0, fastBitmap.BitmapData[row][col].Stride * fastBitmap.BitmapData[row][col].Height, false, true))
            {
                for (int r = 0; r < fastBitmap.BitmapData[row][col].Height; r++)
                {
                    dsOut.WriteRange<byte>(dsIn.ReadRange<byte>(fastBitmap.BitmapData[row][col].Stride));
                    dsIn.Position += dx;
                }
            }
        }
        finally
        {
            device.ImmediateContext.UnmapSubresource(stage, 0);
        }
        dsIn.Dispose();
    }
    return true;
}


来源:https://stackoverflow.com/questions/37099845/convert-slimdx-direct3d11-texture2d-to-net-bitmap

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!