0

I'm trying to copy a managed Bitmap to an unmanaged float array (for usage with Opencl.net wrapper's Cl.CreateImage2D). Unfortunately I'm getting an exception, however if I divide the array length (srcIMGBytesSize) by 4, I'm succeeding. Is there a problem with my array's length? The image format is Format32bppArgb. I'm using mono.

System.Drawing.Bitmap bmpImage = new System.Drawing.Bitmap(inputImage);
bitmapData = bmpImage.LockBits( new Rectangle(0, 0, bmpImage.Width, bmpImage.Height), ImageLockMode.ReadOnly, inputImage.PixelFormat);
IntPtr srcBmpPtr = bitmapData.Scan0;
int bitsPerPixel = Image.GetPixelFormatSize( inputImage.PixelFormat );
srcIMGBytesSize = bitmapData.Stride * bitmapData.Height;
float[] srcImage2DData = new float[srcIMGBytesSize];
Marshal.Copy(srcBmpPtr, srcImage2DData, 0, srcIMGBytesSize);   //Exception at this line
bmpImage.UnlockBits( bitmapData );

I'm getting the following exception when trying to copy the data into float[] array:

System.Runtime.InteropServices.SEHException (0x80004005): External component has thrown an exception.
   at System.Runtime.InteropServices.Marshal.CopyToManaged(IntPtr source, Object destination, Int32 startIndex, Int32 length)
   at System.Runtime.InteropServices.Marshal.Copy(IntPtr source, Single[] destination, Int32 startIndex, Int32 length)

Thank you!

Charles
  • 50,943
  • 13
  • 104
  • 142
Ilya Suzdalnitski
  • 52,598
  • 51
  • 134
  • 168

1 Answers1

2

Check out this link from MSDN

Unmanaged, C-style arrays do not contain bounds information, which prevents the startIndex and length parameters from being validated. Thus, the unmanaged data corresponding to the source parameter populates the managed array regardless of its usefulness. You must initialize the managed array with the appropriate size before calling this method.

Basically you are trying to copy byte array to a float array, each float (Single) is of size 4 bytes, therefore, each four bytes from your unmanaged array will be stored in the one float value using Marshal.Copy, you can check this by executing the following code:

byte[] byteSrcImage2DData = new byte[srcIMGBytesSize];
Marshal.Copy(srcBmpPtr, byteImage2DData, 0, srcIMGBytesSize);

it works because the whole source array will use all the fields of the destination array unlike in your first attempt where you are using only the first quarter.

You could solve your problem by using this code. You can copy your unmanaged array first to the byte array and then you can copy byte array to the float array:

System.Drawing.Bitmap bmpImage = new System.Drawing.Bitmap(inputImage);
BitmapData bitmapData = bmpImage.LockBits(new Rectangle(0, 0, bmpImage.Width, bmpImage.Height), ImageLockMode.ReadOnly, inputImage.PixelFormat);
IntPtr srcBmpPtr = bitmapData.Scan0;
int bitsPerPixel = Image.GetPixelFormatSize(inputImage.PixelFormat);
int srcIMGBytesSize = bitmapData.Stride * bitmapData.Height;
byte[] byteSrcImage2DData = new byte[srcIMGBytesSize];
Marshal.Copy(srcBmpPtr, byteSrcImage2DData, 0, srcIMGBytesSize);
float[] srcImage2DData = new float[srcIMGBytesSize];
Array.Copy(byteSrcImage2DData, srcImage2DData,srcIMGBytesSize);   //Exception at this line
bmpImage.UnlockBits(bitmapData);
Nikola Davidovic
  • 8,556
  • 1
  • 27
  • 33
  • Thanks a lot, this makes a lot of sense! – Ilya Suzdalnitski Nov 28 '12 at 13:58
  • 1
    @Ilya: If using the pointer on the stack, you could probably skip the last 3 lines if you go unsafe. – leppie Nov 28 '12 at 14:09
  • One more question - how would I go about converting the final array to IntPtr? So basically what I'm trying to do is to convert bitmapData.scan0 from a pointer to byte[] to a pointer to float[]. – Ilya Suzdalnitski Nov 28 '12 at 14:16
  • 1
    Check [this answer](http://stackoverflow.com/questions/537573/how-to-get-intptr-from-byte-in-c-sharp) on stack, you should only create array of floatArray.Length*4 – Nikola Davidovic Nov 28 '12 at 15:16