-1

I develop a .NET WinForms application and need to scale a lot of big images and display them as small icons on the form. I have problem with performance especially on specific machines. Thus my goal now is to use SharpDX to scale these image using Direct2D. Somehow images are scaled now, but I cannot copy image from DirectX bitmap to .NET one.

Private Sub CopyBitmapFromDxToGDI(bitmap As d2.Bitmap1, bmp As Drawing.Bitmap)
   Dim d2dBitmapProps2 = New d2.BitmapProperties1(d2PixelFormat, 96, 96, BitmapOptions.CpuRead Or BitmapOptions.CannotDraw  )
   Dim d2dRenderTarget2 = New d2.Bitmap1(d2dContext, New Size2(bmp.Width, bmp.Height), d2dBitmapProps2)
   d2dRenderTarget2.CopyFromRenderTarget(d2dContext)
   Dim surface = d2dRenderTarget2.Surface
   Dim dataStream As DataStream = Nothing
   surface.Map(dxgi.MapFlags.Read, dataStream)
   Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(
      New System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
      System.Drawing.Imaging.ImageLockMode.ReadWrite,
      System.Drawing.Imaging.PixelFormat.Format32bppPArgb)

   Dim offset = 3
   Debug.WriteLine($"({surface.Description.Width}, {surface.Description.Height})")
   For y As Integer = 0 To surface.Description.Height - 1         
      For x As Integer = 0 To surface.Description.Width -1         
         Dim b As Byte = dataStream.Read(Of Byte)()
         Dim g As Byte = dataStream.Read(Of Byte)()
         Dim r As Byte = dataStream.Read(Of Byte)()
         Dim a As Byte = dataStream.Read(Of Byte)()            

         Marshal.WriteByte(bmpData.Scan0, offset, a)
         offset += 1
         Marshal.WriteByte(bmpData.Scan0, offset, r)
         offset += 1
         Marshal.WriteByte(bmpData.Scan0, offset, g)
         offset += 1
         Marshal.WriteByte(bmpData.Scan0, offset, b)
         offset += 1
      Next
   Next
   bmp.UnlockBits(bmpData)
   surface.Unmap()
End Sub

The code is rather simple. I create a new bitmap that allows CPU access, get DataStream from it and then write this data to .NET bitmap byte by byte. However, this code mostly does not work. It can render image properly only if the target image have specific size. With specific size it simply crash with AccessViolationException. With specific size it is rendered strange: enter image description here

Do you have an idea what I have missed in my code?

Community
  • 1
  • 1
Drreamer
  • 332
  • 1
  • 10
  • Difficult to see where the problem is since there's not all the code. It's probably a format mismatch between the GDI+ bitmap and the ID2D1Bitmap1 format. Also you must use the Pitch (aka stride) value that's returned from surface.Map. This is the exact width in bytes of the surface, which can be different from the width of the image. – Simon Mourier May 27 '20 at 16:00
  • It looks like the value of `offset` is wrong. Example #2 at [SharpDX.DataStream Examples](https://csharp.hotexamples.com/examples/-/SharpDX.DataStream/-/php-sharpdx.datastream-class-examples.html) shows the offset being calculated for each line as `offset = bitmapData.Stride * y`. – Andrew Morton May 27 '20 at 16:03
  • Thank you @AndrewMorton. The sample helps, now I calculate position in the DataStream for each pixel and this do the trick. dataStream.Seek((y * dataRectangle.Pitch) + (x * 4), SeekOrigin.Begin) dataStream.Read(buffer, 0, 4) – Drreamer May 28 '20 at 08:14

1 Answers1

0

To resolve the problem it is necessary to calculate position of each pixel in the memory stream using dataRectangle.Pitch. The following code works fine now:

Private Sub CopyBitmapFromDxToGDI(bitmap As d2.Bitmap1, bmp As Drawing.Bitmap)
   Dim d2TempBitmapProps = New d2.BitmapProperties1(d2PixelFormat, 96, 96, BitmapOptions.CpuRead Or BitmapOptions.CannotDraw)
   Dim d2TempBitmap = New d2.Bitmap1(d2dContext, New Size2(bmp.Width, bmp.Height), d2TempBitmapProps)      
   d2TempBitmap.CopyFromRenderTarget(d2dContext)

   Dim surface = d2TempBitmap.Surface
   Dim dataStream As DataStream = Nothing
   Dim dataRectangle As DataRectangle = surface.Map(dxgi.MapFlags.Read, dataStream)
   Dim bmpData As Imaging.BitmapData = bmp.LockBits(New Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format32bppPArgb)

   Dim offset = bmpData.Reserved
   Dim buffer(4) As Byte
   For y As Integer = 0 To surface.Description.Height - 1
      For x As Integer = 0 To surface.Description.Width - 1

         dataStream.Seek((y * dataRectangle.Pitch) + (x * 4), SeekOrigin.Begin)
         dataStream.Read(buffer, 0, 4)           

         Marshal.WriteByte(bmpData.Scan0, offset, buffer(3))            
         Marshal.WriteByte(bmpData.Scan0, offset+1, buffer(0))
         Marshal.WriteByte(bmpData.Scan0, offset+2, buffer(1))
         Marshal.WriteByte(bmpData.Scan0, offset+3, buffer(2))
         offset += 4
      Next
   Next
   bmp.UnlockBits(bmpData)
   surface.Unmap()
   dataStream.Dispose()
   d2TempBitmap.Dispose()
End Sub
Drreamer
  • 332
  • 1
  • 10