views:

429

answers:

4

Hello,

unfortunately I cannot resort to C# in my current project, so I'll have to solve this without the unsafe keyword.

I've got a bitmap, and I need to access the pixels and channel values directly. I'd like to go beyond Marshal.ReadByte() and Marshal.WriteByte() (and definitely beyond GetPixel and SetPixel).

Is there a way to put all the pixel data of the bitmap into a Byte array that works on both 32 and 64 bit systems? I want the exact same layout as the original bitmap, so the padding for each row (if it exists) also needs to be included.

Marshal doesn't seem to have something akin to:

byte[] ReadBytes(IntPtr start, int offset, int count)

Unless I totally missed it...

Any help greatly appreciated, David

ps. So far all my images are in 32BppPArgb pixelformat.

+1  A: 

VB does not offer methods for direct memory access. You have two choices:

  1. Use the Marshal class
  2. Write a small unsafe C# (or C++/CLI) library that handles only these operations and reference it from your VB code.

Alright, there is a third option. VB.Net does not inherently support direct memory access, but it can be accomplished. It's just ugly and prone to errors. Nonetheless, if you're willing to put in the effort, you can try building a bitmap access library using these techniques combined with the approach referenced previously.

hemp
How do I use the Marshal class to copy an entire block of bytes in one go to a byte array?As I pointed out in the question Marshal.ReadByte and unsafe are not an option at this time.
David Rutten
You can't do it all in one go using the Marshal class - I'm suggesting you might have to modify your constraints since they appear to be mutually exclusive.However, I updated my answer to add a 3rd (very convoluted) option. If you decide to go that route, please share your pure VB.Net, high performance bitmap library with the world!
hemp
Interesting, that looks like it carries promise. Thanks!
David Rutten
The article linked to is VB6.
Snarfblam
+1  A: 

Would something like this do? (untested):

Public Shared Function BytesFromBitmap(ByVal Image As Drawing.Bitmap) As Byte()
   Using buffer As New IO.MemoryStream()
        image.Save(result, Drawing.Imaging.ImageFormat.Bmp)

        Using rdr As New IO.BinaryReader(buffer)
            Return rdr.ReadBytes(buffer.Length)
        End Using
    End Using
End Function

It won't let you manipulate the pixels in a Drawing.Bitmap object directly, but it will let you copy that bitmap to a byte array, as per the question title.

Another option is serialization via the BinaryFormatter, but I think that will still require you to pass it through a MemoryStream.

Joel Coehoorn
Interesting solution. It might work, but it would only do half the job. How do I get the byte-array back into the bitmap again?
David Rutten
The System.Drawing.Bitmap.FromStream() function.
Joel Coehoorn
You also might be able to do some of your changes to the memorystream directly, and save yourself some steps in there.
Joel Coehoorn
+2  A: 

Marshal does have a Method that does exactly what you are asking. See Marshall.Copy()

public static void Copy(
    IntPtr source,
    byte[] destination,
    int startIndex,
    int length
   )

Copies data from an unmanaged memory pointer to a managed 8-bit unsigned integer array.

And there are overloads to go the other direction as well

shf301
+1, this is really the right way to do this.
Snarfblam
Brilliant! Thanks so much. This is indeed exactly what I was looking for.
David Rutten
+1  A: 

shf301 is right on the money, but I'd like to add a link to a comprehensive explanation/tutorial on fast pixel data access. Rather than saving the image to a stream and accessing a file-in-memory, it would be better to lock the bitmap, copy pixel data out, access it, and copy it back in. The performance of this technique is pretty good.

Code is in c#, but the approach is language-neutral and easy to read.

http://ilab.ahemm.org/tutBitmap.html

Snarfblam
Thanks Snarf, good bed-time reading.
David Rutten