【问题标题】:Loading/Creating an image into SharpDX in a .NET program在 .NET 程序中将图像加载/创建到 SharpDX
【发布时间】:2011-03-01 21:22:50
【问题描述】:

我正在尝试使用 SharpDX 使用 DirectX 创建一个简单的类似迷宫的 2D 程序。

为此,我想为墙壁、走廊、迷宫外等创建可以在屏幕上渲染的位图。

但是,我似乎不知道如何将现有图像文件加载到 SharpDX 库中的 Bitmap 类中,或者如何从头开始创建新的此类 Bitmap 类。

由于据说所有类都直接映射到 DirectX 类型,我想这只是意味着我需要了解更多 DirectX,但我希望有一个简单的例子可以告诉我我需要做什么。

如果我必须从头开始构建一个新的位图并绘制它,我可以做到,获得我需要的像素并不难,但是我什至无法弄清楚那部分。

有人对SharpDX 库有任何经验,可以给我一些指点吗?

【问题讨论】:

    标签: .net image bitmap directx


    【解决方案1】:

    您应该直接向 Sharpdx 项目的“问题”选项卡提出这个问题,因为我可以从那里给您快速回复(您很幸运,我有时会从网上检查它的使用情况;)) .如果你是正式提出这个问题,我可以对库进行改进,并将你的请求记录在问题数据库中。

    关于您的特定问题,尚不清楚您使用的是哪种 DirectX API。我假设您使用的是 Direct2D?

    如果是,则没有 API 可以直接从文件加载 SharpDX.Direct2D1.Bitmap(我将在 API 中添加此方法)。我刚刚上传了一个正在执行此操作的bitmap sample

    /// <summary>
    /// Loads a Direct2D Bitmap from a file using System.Drawing.Image.FromFile(...)
    /// </summary>
    /// <param name="renderTarget">The render target.</param>
    /// <param name="file">The file.</param>
    /// <returns>A D2D1 Bitmap</returns>
    public static Bitmap LoadFromFile(RenderTarget renderTarget, string file)
    {
        // Loads from file using System.Drawing.Image
        using (var bitmap = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(file))
        {
            var sourceArea = new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height);
            var bitmapProperties = new BitmapProperties(new PixelFormat(Format.R8G8B8A8_UNorm, AlphaMode.Premultiplied));
            var size = new System.Drawing.Size(bitmap.Width, bitmap.Height);
    
            // Transform pixels from BGRA to RGBA
            int stride = bitmap.Width * sizeof(int);
            using (var tempStream = new DataStream(bitmap.Height * stride, true, true))
            {
                // Lock System.Drawing.Bitmap
                var bitmapData = bitmap.LockBits(sourceArea, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
    
                // Convert all pixels 
                for (int y = 0; y < bitmap.Height; y++)
                {
                    int offset = bitmapData.Stride*y;
                    for (int x = 0; x < bitmap.Width; x++)
                    {
                        // Not optimized 
                        byte B = Marshal.ReadByte(bitmapData.Scan0, offset++);
                        byte G = Marshal.ReadByte(bitmapData.Scan0, offset++);
                        byte R = Marshal.ReadByte(bitmapData.Scan0, offset++);
                        byte A = Marshal.ReadByte(bitmapData.Scan0, offset++);
                        int rgba = R | (G << 8) | (B << 16) | (A << 24);
                        tempStream.Write(rgba);
                    }
    
                }
                bitmap.UnlockBits(bitmapData);
                tempStream.Position = 0;
    
                return new Bitmap(renderTarget, size, tempStream, stride, bitmapProperties);
            }
        }
    }
    

    正如您所说,SharpDX 提供对原始低级 DirectX API 的访问(不像 XNA 提供更高级别的 API),因此您绝对需要了解如何对原始 DirectX 进行编程才能使用 SharpDX。

    【讨论】:

    • 我让自己为这个解决方案添加一个评论,因为我最近在努力实现类似的功能,可以通过使用 .NET 提供的一些抽象来帮助自己 - 我被低估了低级东西。当您使用 ARGB 像素格式的 LockBits 时,您可能想知道为什么按 BGRA 顺序读取字节 - 那是因为机器的字节序(字节顺序)。可以通过不从 Scan0 指针读取单个字节而是整个 Int32 来抽象这一点,因此 .NET 执行适当的转换,然后您可以使用 Color.FromArgb 解析它并提取组件字节
    【解决方案2】:

    当我使用 SlimDX 时,我遇到了同样的问题(使用 Direct2D 加载位图)并找到了一个类似的解决方案,它取消了嵌入式循环并且需要更少的代码;将其转换为 SharpDX 是一件简单的事情。 (我希望我能告诉你我在哪里找到了原件,但已经多年了,显然我没有记录来源。据我所知,它可能直接来自 SlimDX 样本。)

    我保留了完整的命名空间,因此您可以准确地知道每种类型的定义位置。此外,一些参数(尤其是 PixelFormat 的参数)是灵活的;与他们一起玩,并使用适合您的任何东西。

    私有位图加载(字符串文件名) { System.Drawing.Bitmap bmp = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(文件名); System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits( 新 System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb); SharpDX.DataStream 流 = new SharpDX.DataStream(bmpData.Scan0, ​​bmpData.Stride * bmpData.Height, true, false); SharpDX.Direct2D1.PixelFormat pFormat = new SharpDX.Direct2D1.PixelFormat(SharpDX.DXGI.Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied); SharpDX.Direct2D1.BitmapProperties bmpProps = new SharpDX.Direct2D1.BitmapProperties(pFormat); SharpDX.Direct2D1.Bitmap 结果 = 新的 SharpDX.Direct2D1.Bitmap( m_renderTarget, 新的 SharpDX.Size2(bmp.Width, bmp.Height), 溪流, bmpData.Stride, bmpProps); bmp.UnlockBits(bmpData); 流.Dispose(); bmp.Dispose(); 返回结果; }

    如您所见,它锁定位图流的方式与 Alexandre 的方法非常相似(在相关的 SharpDX 示例项目中使用),但不是手动复制每个像素,而是构造函数本身在幕后复制流。我没有将性能与 Alexandre 提出的方法进行比较,所以我无法告诉你哪种方法更快,但这个方法对于我的目的来说已经足够快了,而且代码很干净。

    (抱歉缺少语法高亮显示; 标签出于某种原因将我的 sn-p 分成多个部分。)

    【讨论】:

      【解决方案3】:

      这是 Alexandre 在 VB 中的方法:

      '/// <summary>
      '/// Loads a Direct2D Bitmap from a file using System.Drawing.Image.FromFile(...)
      '/// </summary>
      '/// <param name="renderTarget">The render target.</param>
      '/// <param name="file">The file.</param>
      '/// <returns>A D2D1 Bitmap</returns>
      Public Shared Function LoadFromFile(renderTarget As RenderTarget, file As String) As SharpDX.Direct2D1.Bitmap
      
          '// Loads from file using System.Drawing.Image
          Using bitmap As System.Drawing.Bitmap = System.Drawing.Image.FromFile(file)
      
              Dim sourceArea As New System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height)
              Dim BitmapProperties As New BitmapProperties(New Direct2D1.PixelFormat(DXGI.Format.R8G8B8A8_UNorm, AlphaMode.Premultiplied))
              Dim Size As New System.Drawing.Size(bitmap.Width, bitmap.Height)
              Dim b As Byte
              Dim g As Byte
              Dim r As Byte
              Dim a As Byte
              Dim rgba As Int32
              '// Transform pixels from BGRA to RGBA
              Dim stride As Int32 = bitmap.Width * 4
              Using tempStream As New DataStream(bitmap.Height * stride, True, True)
      
                  '// Lock System.Drawing.Bitmap
                  Dim bitmapData As System.Drawing.Imaging.BitmapData = bitmap.LockBits(sourceArea, ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
      
                  '// Convert all pixels 
                  For y As Int32 = 0 To bitmap.Height - 1
      
                      Dim offset As Int32 = bitmapData.Stride * y
                      For x As Int32 = 0 To bitmap.Width - 1
      
                          '// Not optimized 
                          b = Marshal.ReadByte(bitmapData.Scan0, offset)
                          offset += 1
                          g = Marshal.ReadByte(bitmapData.Scan0, offset)
                          offset += 1
                          r = Marshal.ReadByte(bitmapData.Scan0, offset)
                          offset += 1
                          a = Marshal.ReadByte(bitmapData.Scan0, offset)
                          offset += 1
                          rgba = r Or (g << 8) Or (b << 16) Or (a << 24)
                          tempStream.Write(rgba)
                      Next
      
                  Next
                  bitmap.UnlockBits(bitmapData)
                  tempStream.Position = 0
      
                  Return New Bitmap(renderTarget, New Size2(Size.Width, Size.Height), tempStream, stride, BitmapProperties)
              End Using
          End Using
      End Function
      

      【讨论】:

        【解决方案4】:

        这是 Tom 在 VB 中的示例,稍作修改。

        Private Function ConvertBitmap(m_renderTarget As RenderTarget, bmp As System.Drawing.Bitmap) As SharpDX.Direct2D1.Bitmap
            Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(
                New System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
                System.Drawing.Imaging.ImageLockMode.ReadOnly,
                System.Drawing.Imaging.PixelFormat.Format32bppPArgb)
        
            Dim stream As New SharpDX.DataStream(bmpData.Scan0, bmpData.Stride * bmpData.Height, True, False)
            Dim pFormat As New SharpDX.Direct2D1.PixelFormat(SharpDX.DXGI.Format.B8G8R8A8_UNorm, AlphaMode.Premultiplied)
            Dim bmpProps As New SharpDX.Direct2D1.BitmapProperties(pFormat)
        
            Dim Result As New SharpDX.Direct2D1.Bitmap(
                m_renderTarget,
                New SharpDX.Size2(bmp.Width, bmp.Height),
                stream,
                bmpData.Stride,
                bmpProps)
        
            bmp.UnlockBits(bmpData)
        
            stream.Dispose()
            bmp.Dispose()
        
            Return Result
        End Function
        

        通过在函数调用中提供渲染目标和位图,我可以在 Lambda 语句中使用该函数将位图放置在由 dalance 提供的 D2dControl 中的资源缓存中(在 NuGet 中可用)。此外,由于我将位图作为资源加载到我的项目中,我可以简单地在函数调用中提供它们:

        Public Sub New()
        
            ...
            resCache.Add("MainBitmap", Function(t As RenderTarget) ConvertBitmap(t, My.Resources.ProjectUtumno_full))
        End Sub
        
        Public Overrides Sub Render(target As RenderTarget)
            target.Clear(New RawColor4(1.0F, 1.0F, 1.0F, 1.0F))
        
            ...
        
            target.DrawBitmap(resCache("MainBitmap"), 1.0F, New BitmapInterpolationMode())
        end sub
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-07
          • 2011-06-15
          • 1970-01-01
          • 1970-01-01
          • 2014-01-18
          相关资源
          最近更新 更多