开发者

Loading/Creating an image into SharpDX in a .NET program

I'm trying to use SharpDX to create a simple maze-like 2D program using DirectX.

To that end I want to create bitmaps that I can render on-screen for the walls, hallways, outside of the maze, etc.

However, I can't seem to figure out how to either load an existing image file into the Bitmap class in the SharpDX library, or how to create a new such Bitmap class from scratch.

Since all the classes are said to be mapped directly to DirectX types, I guess this just means I need to learn more DirectX, but I was hoping there was a simple example somewhere that could show me what I need to do.

If I have to construct a new Bitmap from s开发者_如何学Ccratch and draw to it, I can do that, it's not difficult getting the pixels I need right, however I can't even seem to figure out that part.

Does anyone have any experience with the SharpDX library and can give me some pointers?


You should ask directly this question to the "Issue" tab on Sharpdx project, as I would be able to give you a quick response from there (you are lucky that I'm sometimes checking its usage from the net ;) ). If you were asking this officially, I could make an improvement to the library and keep a record of your request in the issue database.

Concerning your particular issue, it's not clear what kind of DirectX API you are using. I assume that you are using Direct2D?

If yes, there is no API to load directly a SharpDX.Direct2D1.Bitmap from a file (I will add this method to the API). I have just uploaded a bitmap sample that is performing this.

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

As you said, SharpDX is giving access to the raw-low-level DirectX API (unlike for example XNA that is giving a higher level API), so you definitely need to understand how program raw DirectX in order to use SharpDX.


Back when I was using SlimDX, I ran across the same issue (loading a bitmap using Direct2D) and found a similar solution that does away with the embedded loop and requires a bit less code; converting it to SharpDX was a simple matter. (I wish I could tell you where I found the original, but it's been years and apparently I didn't document the source. It could be straight out of the SlimDX samples for all I know.)

I left the namespaces intact so you know exactly where each type is defined. Also, some of the parameters (particularly those for PixelFormat) are flexible; play around with them and use whatever works for you.

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

As you can see, it locks the bitmap stream much as Alexandre's approach (which is used in the associated SharpDX sample project), but instead of manually copying each pixel, the constructor itself copies the stream behind the scenes. I haven't compared performance with the method Alexandre has proposed, so I can't tell you which method is faster, but this one is plenty fast enough for my purposes, and the code is clean.

(Sorry for the lack of syntax highlighting; the <code> tag for some reason breaks my snippet into sections.)


Here is Alexandre's approach in 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


And this is Tom's example in VB, with a slight modification.

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

By providing the rendertarget and bitmap in the function call, I can use the function in a Lambda statement to place the bitmap in the resource cache that is provided in the D2dControl by dalance (available in NuGet). Also, since I have my bitmaps loaded as resources in my project, I can simply provide them in the function call:

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜