【问题标题】:Convert SlimDX.Direct3D11 Texture2D to .Net Bitmap将 SlimDX.Direct3D11 Texture2D 转换为 .Net 位图
【发布时间】:2016-05-08 12:29:29
【问题描述】:

将 .Net 位图转换为 SlimDx Texture2D 非常快,如下所示: 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;
}

为了以正确的格式将纹理转换回 .Net 位图,我使用它,但它非常慢:

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;
}

有没有更快的方法???我尝试了很多,例如:

但 DataRectangle 的数据正好是我的 DataStream 中所需数据的 8 倍

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;
}

有人可以帮忙吗? 复制我的数据大约需要整个计算时间的 50%。 如果能解决这个问题,我的 App 会快很多……

【问题讨论】:

    标签: bitmap texture2d slimdx directcompute


    【解决方案1】:

    我使用的转换器是这样的:

        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);
            }
        }
    

    但是,我遇到了 dpi 为 72 而不是 96 的问题,就像我在程序中使用的 BitmapSource 一样。希望对大家有所帮助。

    我也在使用 SlimDX。

    WPF 位图转换从一开始就让人头疼。通过进入不安全代码和锁定位来获得最大速度可能需要从 Pbgra 和 bgra 到 rgba 等的转换。我通常最终回到旧的绘图位图以在许多程序中转换为位图源。这个片段是我正在使用的,直到我可以从我的计算着色器中获取正确的 PixelFormat。

            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;
            }
    

    使用 System.Drawing.Image 或 System.drawing.Bitmap 中的 HBITMAP 可以更好地控制锁定位和不同 PixelFormat 的转换。旧的 CreateBitmap 函数和 DIB 也使用 HBITMAP 进行转换。

    我想象的另一个速度瓶颈是在 Texture2D.ToStream() 例程中。这些 ImageFileFormat.Png、ImageFileFormat.Bmp、ImageFileFormat.Jpg 等。SlimDX 没有在 Texture2D 中添加一些 Copy 功能,而 DX11 在某处有。

    【讨论】:

    • 为了保留 PixelFormat 和 Dpi,我正在使用我的 Temp Bitmap,然后将 PixelData 复制到具有正确格式的图像。
    【解决方案2】:

    我找到了解决方案,感谢:http://www.rolandk.de/wp/2013/06/inhalt-der-rendertarget-textur-in-ein-bitmap-kopieren/

    但故事有点复杂,因为 Texture Pitch 与 Bitmap Stride 不匹配,所以这里是我的解决方案,比我的问题中的快 10 倍:

    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;
    }
    

    【讨论】:

    • 什么是 FastBitmapSingle?我在 Google 上搜索的任何内容都会返回到您的代码。
    • 这是我的位图包装器。只需将其替换为标准位图功能即可。祝你好运。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-28
    • 2018-09-04
    • 2018-11-28
    • 1970-01-01
    相关资源
    最近更新 更多