在.NET程序中将图像加载/创建到SharpDX中

时间:2011-03-01 21:22:50

标签: .net image bitmap directx

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

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

但是,我似乎无法弄清楚如何将现有图像文件加载到SharpDX库中的Bitmap类中,或者如何从头创建一个新的此类Bitmap类。

由于所有类都被称为直接映射到DirectX类型,我想这只是意味着我需要学习更多DirectX,但我希望有一个简单的例子可以告诉我我需要做什么。 / p>

如果我必须从头开始构建一个新的Bitmap并绘制它,我可以做到这一点,获得我需要的像素并不困难,但我似乎无法弄清楚那部分。

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

4 个答案:

答案 0 :(得分:10)

你应该直接问这个问题到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。

答案 1 :(得分:0)

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

我保留了名称空间,因此您可以准确了解每种类型的定义。此外,一些参数(特别是PixelFormat的参数)是灵活的;和他们一起玩,并使用适合你的任何东西。

private Bitmap Load(string filename)
{
    System.Drawing.Bitmap bmp = (System.Drawing.Bitmap)System.Drawing.Image.FromFile(filename);

    System.Drawing.Imaging.BitmapData bmpData =
        bmp.LockBits(
            new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
            System.Drawing.Imaging.ImageLockMode.ReadOnly,
            System.Drawing.Imaging.PixelFormat.Format32bppPArgb);

    SharpDX.DataStream stream = 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 result =
        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;
}

正如您所看到的,它将位图流锁定为Alexandre的方法(在相关的SharpDX示例项目中使用),但不是手动复制每个像素,构造函数本身会在后台复制流。我没有将性能与Alexandre提出的方法进行比较,所以我不能告诉你哪种方法更快,但是这个方法对我的目的而言足够快,而且代码很干净。

(很抱歉缺少语法高亮显示;&lt; code&gt;标记由于某种原因会将我的代码段分成几部分。)

答案 2 :(得分:0)

这是VB中Alexandre的方法:

'/// <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

答案 3 :(得分:0)

这是汤姆(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

通过在函数调用中提供rendertarget和bitmap,我可以在Lambda语句中使用该函数将damap放置在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