简体   繁体   中英

Converting System.drawing to SharpDX.direct 2D1

I am working on a graphics application that can draw on the CPU or the GPU depending on users setting. To draw on CPU, I am using GDI+ technologies with System.Drawing component. To draw on GPU, I want to use SharpDX.Direct2D1 (because I am using c#).

I made a DrawingContext abstract class which implements every function of drawing of Graphics class(receiving System.Drawing.Brush, System.Drawing.Rectangle, etc... as parameters) and reimplements them into it's derived class (CPUDrawingContext). Now, I have a GPUDrawingContext class that has to overrides all of these methods, but since the parameters are from type System.Drawing, I need to convert them into sharpDX component, very fast so we can't see the difference.

See a little example here : i've only put some portion of code so you can see the concept.

DrawingContext

Public abstract class DrawingContext {
//System.drawing.Bitmap, System.Drawing.RectangleF
Public asbstract DrawImage(Bitmap b, RectangleF dest, RectangleF source,GraphicsUnit g);
//System.Drawing.Brush
Public abstract DrawRectangle(Brush b, Rectangle rect);


}

CPUDrawingContext: using graphicContext As Graphics to render on screen

Public  class CPUDrawingContext{
  //System.drawing.Bitmap, System.Drawing.RectangleF
  Public override DrawImage(Bitmap b, RectangleF dest, RectangleF source,GraphicsUnit g);
  //System.Drawing.Brush
  Public override DrawRectangle(Brush b, Rectangle rect){
      graphicContext.Rectangle(b,rect);
  }
  Public override DrawImage(Bitmap b, RectangleF dest, RectangleF source, GraphicsUnit g) {
      graphicContext.DrawImage(b,dest,source,g);
  }
}

GPUDrawingContext : using renderTarger As RenderTarget to render on screen

Public class GPUDrawingContext {
   //System.drawing.Bitmap, System.Drawing.RectangleF
  Public override DrawImage(Bitmap b, RectangleF dest, RectangleF  source,GraphicsUnit g);
  //System.Drawing.Brush
  Public override DrawRectangle(Brush b, Rectangle rect){
      //convert b and rect to fit sharpDX component
      renderTarget.DrawRectangle(b,rect);
   }
  Public override DrawImage(Bitmap b, RectangleF dest, RectangleF source, GraphicsUnit g) {
      //(convert b,dest,source and g to fit sharpDX component)
      renderTarget.DrawImage(b,dest,source,g);
   }
 }

I have commented into GPUDrawingContext the area that I need to convert before drawing.

My question is, is it possible to do that very fast so we won't notice (like conversion of less than 10ms).

Since my application will need to draw Bitmap, I need to convert System.Drawing.Bitmap into SharpDX.Direct2D1.Bitmap very fast but i've noticed sharpDX bitmap does not seem really compatible with System.Drawing.Bitmap.

So this is how I proceed to convert a System.Drawing.Bitmap to a SharpDX.Direct2d1.Bitmap.

Dim bmpData As BitmapData = m_Bitmap.LockBits(New System.Drawing.Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, m_Bitmap.PixelFormat)

  Dim bgraArray As Byte() = New Byte(bmpData.Width * bmpData.Height * 4 - 1) {}

  Dim scan As IntPtr = bmpData.Scan0
  Marshal.Copy(scan, bgraArray, 0, (bmpData.Width * bmpData.Height) * 4)

  Dim rgbaarray As Byte() = New Byte(bmpData.Width * bmpData.Height * 4 - 1) {}

  Parallel.For(0, 4, Sub(range)
                        For i As Integer = range * bmpData.Height / 4 To bmpData.Height / 4 * (range + 1) - 1
                           Dim offset As Integer = i * bmpData.Width * 4
                           For j As Integer = 0 To bmpData.Width * 4 - 1 Step 4
                              rgbaarray(offset + j) = bgraArray(offset + j + 2)
                              rgbaarray(offset + j + 1) = bgraArray(offset + j + 1)
                              rgbaarray(offset + j + 2) = bgraArray(offset + j)
                              rgbaarray(offset + j + 3) = bgraArray(offset + j + 3)
                           Next
                        Next
                     End Sub)

  Dim stream As DataStream = New DataStream(bmpData.Height * bmpData.Width * 4, True, True)
  stream.WriteRange(rgbaarray)
  stream.Position = 0
  m_sharpDXbitmap = New SharpDX.Direct2D1.Bitmap(deviceContext, New Size2(m_Bitmap.Width, m_Bitmap.Height), stream, bmpData.Width * 4, bitmapProperties)
  m_Bitmap.UnlockBits(bmpData)

  stream.Dispose()

However, this is not taking 10ms but is taking 120ms. What I did to keep the abstraction that I wanted is to create a CustomBitmap class that contains both bitmaps, and on the first draw call, if it has not been converted yet, convert it only once, so on futur draw calls, this is going to be instant. Here is my CustomBitmap class :

Imports System.Drawing.Imaging
Imports System.Reflection
Imports SharpDX
Imports SharpDX.Direct2D1
Imports System.Runtime.InteropServices
Imports Synergx.Common.Drawing
Imports System.Collections.Concurrent
Imports System.Threading.Tasks

Public Class CustomBitmap

   Private bitmapProperties As BitmapProperties = New BitmapProperties(New SharpDX.Direct2D1.PixelFormat(SharpDX.DXGI.Format.R8G8B8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Ignore))

   Public Property Bitmap As System.Drawing.Bitmap
      Get
         Return m_Bitmap
      End Get
      Set(value As System.Drawing.Bitmap)
         m_Bitmap = value
      End Set
   End Property

   Private m_Width As Integer
   Private m_Height As Integer
   Private m_Bitmap As System.Drawing.Bitmap
   Private m_sharpDXbitmap As SharpDX.Direct2D1.Bitmap = Nothing

   Public ReadOnly Property Width As Integer
      Get
         Return m_Width
      End Get
   End Property

   Public ReadOnly Property Height As Integer
      Get
         Return m_Height
      End Get
   End Property

   Public ReadOnly Property GPUBitmap As SharpDX.Direct2D1.Bitmap
      Get
         Return m_sharpDXbitmap
      End Get
   End Property

   Public Sub New(bitmap As System.Drawing.Bitmap)
      Me.Bitmap = bitmap
      m_Width = bitmap.Width
      m_Height = bitmap.Height
   End Sub

   Friend Sub GenerateSharpDXBitmap(deviceContext As SharpDX.Direct2D1.DeviceContext)

      If (m_Bitmap.PixelFormat = Imaging.PixelFormat.Format8bppIndexed) Then
         GenerateSharpDXBitmap8bpp(deviceContext)
      ElseIf (m_Bitmap.PixelFormat = Imaging.PixelFormat.Format32bppArgb) Then
         GenerateSharpDXBitmap32argp(deviceContext)
      End If

   End Sub

   Private Sub GenerateSharpDXBitmap8bpp(deviceContext As SharpDX.Direct2D1.DeviceContext)

      Dim bmpData As BitmapData = m_Bitmap.LockBits(New System.Drawing.Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, m_Bitmap.PixelFormat)

      Dim integerArray As Integer()
      ReDim integerArray(bmpData.Height * bmpData.Width - 1)

      Parallel.For(0, 4, Sub(range)
                            Dim scan As IntPtr = bmpData.Scan0

                            For i As Integer = 0 To bmpData.Height * range / 4 - 1 - 1
                               scan += bmpData.Stride
                            Next

                            For y As Integer = bmpData.Height * range / 4 To bmpData.Height * (range + 1) / 4 - 1
                               Dim bytes As Byte() = New Byte(bmpData.Width - 1) {}
                               Marshal.Copy(scan, bytes, 0, bmpData.Width)

                               For x As Integer = 0 To bytes.Length - 1
                                  Dim B As Integer = bytes(x)
                                  Dim rgba As Integer = B Or (B << 8) Or (B << 16) Or (B << 24)
                                  integerArray(x + y * bmpData.Width) = rgba
                               Next
                               scan += bmpData.Stride

                            Next
                         End Sub)

      Dim stream As DataStream = New DataStream(bmpData.Height * bmpData.Width * 4, True, True)
      stream.WriteRange(integerArray)
      stream.Position = 0
      m_sharpDXbitmap = New SharpDX.Direct2D1.Bitmap(deviceContext, New Size2(m_Bitmap.Width, m_Bitmap.Height), stream, bmpData.Width * 4, bitmapProperties)
      m_Bitmap.UnlockBits(bmpData)

      stream.Dispose()

   End Sub

   Private Sub GenerateSharpDXBitmap32argp(deviceContext As SharpDX.Direct2D1.DeviceContext)
      Dim bmpData As BitmapData = m_Bitmap.LockBits(New System.Drawing.Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, m_Bitmap.PixelFormat)

      Dim bgraArray As Byte() = New Byte(bmpData.Width * bmpData.Height * 4 - 1) {}

      Dim scan As IntPtr = bmpData.Scan0
      Marshal.Copy(scan, bgraArray, 0, (bmpData.Width * bmpData.Height) * 4)

      Dim rgbaarray As Byte() = New Byte(bmpData.Width * bmpData.Height * 4 - 1) {}

      Parallel.For(0, 4, Sub(range)
                            For i As Integer = range * bmpData.Height / 4 To bmpData.Height / 4 * (range + 1) - 1
                               Dim offset As Integer = i * bmpData.Width * 4
                               For j As Integer = 0 To bmpData.Width * 4 - 1 Step 4
                                  rgbaarray(offset + j) = bgraArray(offset + j + 2)
                                  rgbaarray(offset + j + 1) = bgraArray(offset + j + 1)
                                  rgbaarray(offset + j + 2) = bgraArray(offset + j)
                                  rgbaarray(offset + j + 3) = bgraArray(offset + j + 3)
                               Next
                            Next
                         End Sub)

      Dim stream As DataStream = New DataStream(bmpData.Height * bmpData.Width * 4, True, True)
      stream.WriteRange(rgbaarray)
      stream.Position = 0
      m_sharpDXbitmap = New SharpDX.Direct2D1.Bitmap(deviceContext, New Size2(m_Bitmap.Width, m_Bitmap.Height), stream, bmpData.Width * 4, bitmapProperties)
      m_Bitmap.UnlockBits(bmpData)

      stream.Dispose()
   End Sub

   Public Sub Dispose()
      If Bitmap IsNot Nothing Then
         m_Bitmap.Dispose()
         m_Bitmap = Nothing
      End If
      If m_sharpDXbitmap IsNot Nothing Then
         m_sharpDXbitmap.Dispose()
         m_sharpDXbitmap = Nothing
      End If
   End Sub

End Class

The class contains 2 different methods that can separe to grayscale colors or to 32bpp depending on the original PixelFormat of your image.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM