Reputation:
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.
Upvotes: 1
Views: 865
Reputation: 41
Just a small correction to user7970127's code:
The inner loop in the Parallel.For
should look like this, otherwise the colours are copied incorrectly.
For j As Integer = 0 To bmpData.Width * 4 - 1 Step 4
rgbaarray(offset + j) = bgraArray(offset + j)
rgbaarray(offset + j + 1) = bgraArray(offset + j + 1)
rgbaarray(offset + j + 2) = bgraArray(offset + j + 2)
rgbaarray(offset + j + 3) = bgraArray(offset + j + 3)
Next
Apart from that, it works correctly. With Direct2D intended as a replacement for GDI+/System.Drawing.Graphics, it really should be easier to migrate a System.Drawing.Bitmap to a Direct2D1.Bitmap.
Upvotes: 0
Reputation:
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.
Upvotes: 1