Search code examples
c#wpfdirectx-9intptr

Show A D3DImage With SharpDX


I intend to develop an application to get an image from a file and show it with Directx9 . I have used this code but I got the Error on SetBackBuffer Method!!

System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.



     private void d3dImage_IsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
            {
                if (d3dImage.IsFrontBufferAvailable)
                {

                    // (Re)initialization:
                    Bitmap b = new Bitmap(@"C:\Users\newuser\Desktop\3.jpg");
                    IntPtr Mysurface = b.GetHbitmap();

                    if (Mysurface != IntPtr.Zero)
                    {
                        d3dImage.Lock();
//Error occurred this line 
                   d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, Mysurface);
                        d3dImage.Unlock();
                        CompositionTarget.Rendering += CompositionTarget_Rendering;
                    }
                }
                else
                {
                    // Cleanup:
                    CompositionTarget.Rendering -= CompositionTarget_Rendering;
                    //Sample.Cleanup();
                }
            }


      protected override void OnSourceInitialized(EventArgs e)
            {
                base.OnSourceInitialized(e);
                // Now that we can get an HWND for the Window, force the initialization
                // that is otherwise done when the front buffer becomes available:
                d3dImage_IsFrontBufferAvailableChanged(this,
                new DependencyPropertyChangedEventArgs());
            }


      private void CompositionTarget_Rendering(object sender, EventArgs e)
            {
                if (d3dImage.IsFrontBufferAvailable)
                {
                    d3dImage.Lock();
                    //Sample.Render();
                    // Invalidate the whole area:
                    d3dImage.AddDirtyRect(new Int32Rect(0, 0,
                    d3dImage.PixelWidth, d3dImage.PixelHeight));
                    d3dImage.Unlock();
                }
            }

actually I want show an image in d3dimage in a WPF project. Can any one help me? I could not find good resources about Directx and C#!!!

Edited Using Sharpdx and Got new Error "'HRESULT: [0x8876086C], Module: [SharpDX.Direct3D9], ApiCode: [D3DERR_INVALIDCALL/InvalidCall], "

s = new SharpDX.Direct3D9.Surface(IntPtr.Zero);

 SharpDX.Direct3D9.Surface.FromFile(s, @"C:\Users\newuser\Desktop\3.jpg", SharpDX.Direct3D9.Filter.None, 1);

Solution

  • You're setting the back buffer to a Bitmap. You cannot do this. The back buffer must be of type IDirect3DSurface9. This is noted in the documentation for the SetBackBuffer method.

    In order to use DirectX9 (unmanaged) from C# (managed), you need to use a wrapper library. Are you using one? If not, there are several available. SlimDX and SharpDX seem to be the most popular. Using any of the wrapper libraries, you can create a IDirect3DSurface9that you render to with a IDirect3DDevice9, and attach that surface to your WPF control. Your rendered content will then appear in the control.

    I think using DirectX may be overkill for what you need, but I'm not sure what the entire scope of your project is.

    Solution using SharpDX

    I put together a working example that uses SharpDX to display an image within a D3DImage. This is very basic and doesn't include proper error handling. It's just meant to get you going in the right direction.

    Step 1 - Create D3D9 device

    To create the necessary surfaces, you will need a Direct3D 9 Device. The following code will create a Device using SharpDX.

    //Create the d3d interface.
    Direct3D d3d = new Direct3D();
    
    //Get a handle to the WPF window. This is required to create a device.
    IntPtr windowHandle = new WindowInteropHelper(this).Handle;
    
    //Create a device. Using standard creation param. 
    //Width and height have been set to 1 because we wont be using the backbuffer.
    //Adapter 0 = default adapter.
    PresentParameters presentationParams = new PresentParameters(1,1);
    Device d3dDevice = new Device(d3d, 0, DeviceType.Hardware, windowHandle, CreateFlags.HardwareVertexProcessing, presentationParams);
    

    Step 2 - Create the image surface

    Create a surface to hold the image data.

    //Create an empty offscreen surface. Use SystemMemory to allow for surface copying.
    Surface imageSurface = Surface.CreateOffscreenPlain(d3dDevice, ImageWidthPixels, ImageHeightPixels, Format.A8R8G8B8, Pool.SystemMemory);
    
    //Fill the surface with the image data.
    Surface.FromFile(imageSurface, "dx.jpg", Filter.None, 0);
    

    Step 3 - Create the render target surface

    Create the surface that acts as a render target. This is the type of surface required by D3DImage.

    //Create the surface that will act as the render target.
    //Set as lockable (required for D3DImage)
    Surface target = Surface.CreateRenderTarget(d3dDevice, ImageWidthPixels, ImageHeightPixels, Format.A8R8G8B8, MultisampleType.None, 0, true);
    

    Step 4 - Copy the image surface to the render target.

    The image surface can be copied into the render target surface using the Device.UpdateSurfaces method. Note that in the previous steps, both surfaces were created with the same format (A8R8G8B). Watch out for this. It's necessary for surface copies.

    //Copy the image surface contents into the target surface.
    d3dDevice.UpdateSurface(imageSurface, target);
    

    Step 5 - Attach the surface to D3DImage

    You now have a surface that contains your image and that can be used with 'D3DImage`. Make sure you use the target surface, not the image source surface.

    this.wpfImageSource.Lock();
    this.wpfImageSource.SetBackBuffer(D3DResourceType.IDirect3DSurface9, target.NativePointer);
    this.wpfImageSource.AddDirtyRect(new Int32Rect(0, 0, wpfImageSource.PixelWidth, wpfImageSource.PixelHeight));
    this.wpfImageSource.Unlock();
    

    Results

    The image appears!

    [4]: https://i.sstatic.net/OS9Ne.png