Is this the absolute fastest I could possibly copy a Bitmap to a Byte[] in C#?
If there is a speedier way I am dying to know!
const int WIDTH = /* width */;
const int HEIGHT = /* height */;
Bitmap bitmap = new Bitmap(WIDTH, HEIGHT, PixelFormat.Format32bppRgb);
Byte[] bytes = new byte[WIDTH * HEIGHT * 4];
BitmapToByteArray(bitmap, bytes);
private unsafe void BitmapToByteArray(Bitmap bitmap, Byte[] bytes)
{
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, WIDTH, HEIGHT), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
fixed(byte* pBytes = &bytes[0])
{
MoveMemory(pBytes, bitmapData.Scan0.ToPointer(), WIDTH * HEIGHT * 4);
}
bitmap.UnlockBits(bitmapData);
}
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
private static unsafe extern void MoveMemory(void* dest, void* src, int size);
Well, using Marshal.Copy() would be wiser here, that at least cuts out on the (one time) cost of looking up the DLL export. But that's it, they both use the C runtime memcpy() function. Speed is entirely throttled by the RAM bus bandwidth, only buying a more expensive machine can speed it up.
Beware that profiling is tricky, accessing the bitmap data the first time causes page faults to get the pixel data into memory. How long that takes is critically dependent on what your hard disk is doing and the state of the file system cache.
I'm reading into this that you're looking to do pixel manipulation of a bitmap.
So logically, you're wanting to access the pixels as an array in order to do this.
The problem your facing is fundamentally flawed, and the other guys didn't pick up on this - and maybe this is something they can take away too?
Basically, you've gotten so far to be messing with pointers from bitmaps, I'm going to give you one of my hard earned "secrets".
YOU DON'T NEED TO COPY THE BITMAP INTO AN ARRAY. Just use it as it is.
Unsafe pointers are your friend in this case. When you hit "bitmapData.Scan0.ToPointer()" you missed the trick.
So long as you have a pointer to the first pixel, and the Stride, and you're aware of the number of bytes per pixel, then you're on to a winner.
I define a bitmap specifically with 32 bits per pixel (memory efficient for UInt32 access) and I get a pointer to first pixel. I then treat the pointer as an array, and can both read and write pixel data as UInt32 values.
Code speaks louder than words. have a look.
I salute you for making it this far!
This is untested code, but much is copied from my source.
public delegate void BitmapWork(UInt32* ptr, int stride);
/// <summary>
/// you don't want to forget to unlock a bitmap do you? I've made that mistake too many times...
/// </summary>
unsafe private void UnlockBitmapAndDoWork(Bitmap bmp, BitmapWork work)
{
var s = new Rectangle (0, 0, bmp.Width, bmp.Height);
var locker = bmp.LockBits(new Rectangle(0, 0, 320, 200), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
var ptr = (UInt32*)locker.Scan0.ToPointer();
// so many times I've forgotten the stride is expressed in bytes, but I'm accessing with UInt32's. So, divide by 4.
var stride = locker.Stride / 4;
work(ptr, stride);
bmp.UnlockBits(locker);
}
//using System.Drawing.Imaging;
unsafe private void randomPixels()
{
Random r = new Random(DateTime.Now.Millisecond);
// 32 bits per pixel. You might need to concern youself with the Alpha part depending on your use of the bitmap
Bitmap bmp = new Bitmap(300, 200, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
UnlockBitmapAndDoWork(bmp, (ptr, stride) =>
{
var calcLength = 300 * 200; // so we only have one loop, not two. It's quicker!
for (int i = 0; i < calcLength; i++)
{
// You can use the pointer like an array. But there's no bounds checking.
ptr[i] = (UInt32)r.Next();
}
});
}
I would also look at System.Buffer.BlockCopy. This function is also very fast and it might be competetive in this setup as you are copying from managed to managed in your case. Unfortunately I can not provide some performance tests.
I think this is faster (I actually used it):
// Bitmap bytes have to be created via a direct memory copy of the bitmap
private byte[] BmpToBytes_MemStream (Bitmap bmp)
{
MemoryStream ms = new MemoryStream();
// Save to memory using the Jpeg format
bmp.Save(ms, ImageFormat.Jpeg);
// read to end
byte[] bmpBytes = ms.GetBuffer();
bmp.Dispose();
ms.Close();
return bmpBytes;
}
Original Source
Related
I have a method in c# that the only thing it does its LockBits, and then UnlockBits, and the images(input/output, transformed to byte arrays) are different. The one from output has less 100 and something bytes than the one from the input. This happens only with .jpg files. And checking the files in HxD I came to the understanding that it´s removing a part of the header, the exif signature to be exact. But I don't know how and why.
Does someone know what this is doing?
Here's the code:
public Image Validate (image){
BitmapData original = null;
Bitmap originalBMP = null;
try{
originalBMP = image as Bitmap;
original = originalBMP.LockBits(new Rectangle(0, 0,
originalBMP.Width, originalBMP.Height),
ImageLockMode.ReadWrite,
originalBMP.PixelFormat);
originalBMP.UnlockBits(original);
}catch{}
return image;
}
Calling Bitmap.LockBits() followed by Bitmap.UnlockBits() does nothing.
The behavior you observe is because of loading a JPEG image, and then saving it again. JPEG uses a lossy algorithm. So what happens:
You load the JPEG from disk
The JPEG data gets decoded into individual pixels with color information, i.e. a bitmap
You save the bitmap again in the JPEG format, resulting in a different file than #1
You also potentially lose metadata that was present in the JPEG file in doing so. So yes, the file is different and probably smaller, because every time you do this, you lose some pixel data or metadata.
Lockbits/Unlockbits are used to allow the program to manipulate the image data in memory. Nothing more, nothing less. See also the documentation for those methods.
Use the LockBits method to lock an existing bitmap in system memory so that it can be changed programmatically. You can change the color of an image with the SetPixel method, although the LockBits method offers better performance for large-scale changes.
A Rectangle structure that specifies the portion of the Bitmap to lock.
Example:
private void LockUnlockBitsExample(PaintEventArgs e)
{
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150);
}
I’m trying to crop a 24bpp image using memcpy like I read here: cropping an area from BitmapData with C#. The problem I’m having is that it only works when my sourceImage is 32bpp. It gives me a corrupt image when my sourceImage is 24bpp.
class Program
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
static unsafe extern int memcpy(byte* dest, byte* src, long count);
static void Main(string[] args)
{
var image = new Bitmap(#"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\adsadas.png");
//Creates a 32bpp image - Will work eventhough I treat it as a 24bpp image in the CropBitmap method...
//Bitmap newBitmap = new Bitmap(image);
//Creates a 24bpp image - Will produce a corrupt cropped bitmap
Bitmap newBitmap = (Bitmap)image.Clone();
var croppedBitmap = CropBitmap(newBitmap, new Rectangle(0, 0, 150, 150));
croppedBitmap.Save(#"C:\Users\Vincent\Desktop\CroppedScaledBitmaps\PieceOfShit.png", ImageFormat.Png);
Console.ReadLine();
}
static public Bitmap CropBitmap(Bitmap sourceImage, Rectangle rectangle)
{
Console.WriteLine("Bits per pixel of sourceImage: {0}", Image.GetPixelFormatSize(sourceImage.PixelFormat));
var sourceBitmapdata = sourceImage.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
var croppedImage = new Bitmap(rectangle.Width, rectangle.Height, PixelFormat.Format24bppRgb);
var croppedBitmapData = croppedImage.LockBits(new Rectangle(0, 0, rectangle.Width, rectangle.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
unsafe
{
byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer();
byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer();
memcpy(croppedImagePointer, sourceImagePointer, croppedBitmapData.Stride * rectangle.Height);
}
sourceImage.UnlockBits(sourceBitmapdata);
croppedImage.UnlockBits(croppedBitmapData);
return croppedImage;
}
}
I’m very confused, because the only thing I’m changing is the sourceImage PixelFormat, not any of the code in the CropBitmap method. So I always call LockBits using 24bpp Pixelformat, even if the sourceImage is 32bpp.
I’ve tried different methods of calculating the number of bytes I’m copying but everything resulted in more or less the same corrupted image.
Any help is appreciated!
You are trying to copy the data as if it was one continuous block, but it isn't.
The image data is arranged in scan lines, but as you are selecting a part of the image, you don't want all the data from each scan line, you only want the data that represents the pixels that you have selected. A scan line contains the data for the pixels that you specified when you called LockBits, but also data for the pixels outside that area.
The Stride value is the difference in memory address from one scan line to the next. The Stride value may also include padding between the scan lines. Note also that the Stride value can be negative, which happens when the image data is stored upside down in memory.
You want to copy the relevant data from one line of the source image to the line in the destination image. As there can be gaps both in the source data and destination data, you can't copy the data as a single chunk of data.
You would need to loop through the lines and copy each line separately, I haven't tested this code, but something like this:
byte* sourceImagePointer = (byte*)sourceBitmapdata.Scan0.ToPointer();
byte* croppedImagePointer = (byte*)croppedBitmapData.Scan0.ToPointer();
int width = rectange.Width * 3; // for 24 bpp pixel data
for (int y = 0; y < rectangle.Height; y++) {
memcpy(croppedImagePointer, sourceImagePointer, width);
sourceImagePointer += sourceBitmapdata.Stride;
croppedImagePointer += croppedBitmapData.Stride;
}
I need to render a 1027 * 768 bitmap to the client window (same size) and I don't have more than 10-15 ms to complete this task. I am using bufferedGraphics allocated from a bufferedGraphicsContect object and still notice huge performance issues.
I am using unsafe code to perform my copy operations and found unbelievable results. I know the Graphics / BufferedGraphics objects should have some sort of a drawing surface in memory. I was wondering if someone could point me in the right direction on how to write to this surface using Marshal or some other unsafe low level method.
I am in the process of porting an older c# graphics application. I know c# is not designed for heavy graphics and that there are better tools than GDI+ available, unfortunately I don't have those luxuries.
This is what I have come up with so far... Any insight what so ever is greatly apperciated.
byte[] _argbs = null;
static readonly Bitmap _bmUnderlay = Properties.Resources.bg;
static Bitmap _bmpRender = new Bitmap(1024, 768, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
int bmpHeight = Properties.Resources.bg.Height;
int bmpWidth = Properties.Resources.bg.Width;
static BufferedGraphicsContext _bgc = new BufferedGraphicsContext();
internal unsafe void FillBackBuffer(Point cameraPos)
{
// lock up the parts of the original image to read (parts of it)
System.Drawing.Imaging.BitmapData bmd = _bmUnderlay.LockBits(
new Rectangle(cameraPos.X, cameraPos.Y, 1024, 768),
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// get the address of the first line.
IntPtr ptr = bmd.Scan0;
//if (_argbs == null || _argbs.Length != bmd.Stride * bmd.Height)
// _argbs = new byte[bmd.Stride * bmd.Height];
if (_argbs == null || _argbs.Length != 1024 * 3 * 768)
_argbs = new byte[1024 * 3 * 768];
// copy data out to a buffer
Marshal.Copy(ptr, _argbs, 0, 1024 * 3 * 768);
_bmUnderlay.UnlockBits(bmd);
// lock the new image to write to (all of it)
System.Drawing.Imaging.BitmapData bmdNew = _bmpRender.LockBits(
new Rectangle(0, 0, 1024, 768),
System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// copy data to new bitmap
Marshal.Copy(_argbs, 0, bmdNew.Scan0, 1024 * 3 * 768);
_bmpRender.UnlockBits(bmdNew);
}
private unsafe void _btnGo_Click(object sender, EventArgs e)
{
// less than 2 ms to complete!!!!!!!!
FillBackBuffer(new Point());
using (BufferedGraphics bg = _bgc.Allocate(CreateGraphics(), ClientRectangle))
{
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
/////
///
// This method takes over 17 ms to complete
bg.Graphics.DrawImageUnscaled(_bmpRender, new Point());
//
///
/////
sw.Start();
this.Text = sw.Elapsed.TotalMilliseconds.ToString();
bg.Render();
}
}
EDIT:
Forgot to mention I'm looking for a low level alternative to Graphics.DrawImage(), preferrably writing to Graphics surface memory, using pointers? Thanks again
Pay attention to the pixel format of the bitmap. On standard 32bpp video hardware, Format32bppPArgb renders ten times faster than any of the other ones. Because the pixels don't need any translation. The 24bpp format you use now needs to be widened to 32bpp and that doesn't come for free. Don't skip the P of PArgb and don't forget to set the alpha value to 255 in your code.
Using BufferedGraphics is fishy btw. You should always use the one you get for free in the OnPaint method. And you probably don't need one at all since you're blitting this fast. That's an automatic 2x speed-up.
I'm coding a live control/remote desktop solution using DFMirage's free mirror driver. There is a C# sample on how to interface and control the mirror driver here. You would need the mirror driver installed first, of course, here. So, the concept is, the client (helper) requests a screen update, and the server (victim) sends one, using raw pixel encoding. The concept of a mirror driver eliminates the need to expensively poll for screen changes, because a mirror driver is notified of all screen drawing operations in real-time. The mirror driver receives the location and size of the update rectangle, and can simply query memory for the new pixel bytes and send them.
Should be easy, except that I don't know how to do that part where we query memory for the new pixel bytes. The sample shows how to query memory to grab the pixels of the entire screen using something with raw bitmap data and scan lines and stride and all that good stuff:
Bitmap result = new Bitmap(_bitmapWidth, _bitmapHeight, format);
Rectangle rect = new Rectangle(0, 0, _bitmapWidth, _bitmapHeight);
BitmapData bmpData = result.LockBits(rect, ImageLockMode.WriteOnly, format);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * _bitmapHeight;
var getChangesBuffer = (GetChangesBuffer)Marshal
.PtrToStructure(_getChangesBuffer, typeof (GetChangesBuffer));
var data = new byte[bytes];
Marshal.Copy(getChangesBuffer.UserBuffer, data, 0, bytes);
// Copy the RGB values into the bitmap.
Marshal.Copy(data, 0, ptr, bytes);
result.UnlockBits(bmpData);
return result;
This is great and works fine. The resulting Bitmap object now has the pixels of the entire screen. But if I wanted to just extract a rectangle of pixel data instead of getting the pixel data from the whole screen, how would I be able to do that? I guess this is more of a rawbitmap-scan-stride question, but I typed all of this so you might know where this is coming from. So any insight on how to get just a portion of pixel data instead of the entire screen's pixel data?
Update: Found something interesting (code portion only).
Here's a function to copy a rectangular area from some source image buffer to a Bitmap:
private static Bitmap ExtractImageRectangle(byte[] sourceBuffer, int sourceStride, PixelFormat sourcePixelFormat, Rectangle rectangle)
{
Bitmap result = new Bitmap(rectangle.Width, rectangle.Height, sourcePixelFormat);
BitmapData resultData = result.LockBits(new Rectangle(0, 0, result.Width, result.Height), ImageLockMode.WriteOnly, result.PixelFormat);
int bytesPerPixel = GetBytesPerPixel(sourcePixelFormat); // Left as an exercise for the reader
try
{
// Bounds checking omitted for brevity
for (int rowIndex = 0; rowIndex < rectangle.Height; ++rowIndex)
{
// The address of the start of this row in the destination image
IntPtr destinationLineStart = resultData.Scan0 + resultData.Stride * rowIndex;
// The index at which the current row of our rectangle starts in the source image
int sourceIndex = sourceStride * (rowIndex + rectangle.Top) + rectangle.Left * bytesPerPixel;
// Copy the row from the source to the destination
Marshal.Copy(sourceBuffer, sourceIndex, destinationLineStart, rectangle.Width * bytesPerPixel);
}
}
finally
{
result.UnlockBits(resultData);
}
return result;
}
You could then use it like this:
Rectangle roi = new Rectangle(100, 150, 200, 250);
Bitmap result = ExtractImageRectangle(getChangesBuffer.UserBuffer, getChangesBuffer.Stride, getChangesBuffer.PixelFormat, roi);
This assumes that GetChangesBuffer has properties for the stride and pixelformat of the source image buffer. It most likely hasn't, but you should have some means to determine the stride and pixel format of your input image. In your example you are assuming the stride of the input image is equal to the stride of your output image, which is a tricky assumption.
I am working on a C# application that would display live images from a camera.
The problem I am facing with the following code snippet is that, I get AccessViolationException in Marshal.Copy when running this function executed continuously in a thread. But, this runs successfully when run once (I get a single static image). I guess it has to do with some memory corruption issue. Any idea/suggestions on how to deal with this problem?
private Image ByteArrayToImage(byte[] myByteArray)
{
if (myByteArray != null)
{
MemoryStream ms = new MemoryStream(myByteArray);
int Height = 504;
int Width = 664;
Bitmap bmp = new Bitmap(Width, Height, PixelFormat.Format24bppRgb);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(myByteArray, 0, bmpData.Scan0, myByteArray.Length);
bmp.UnlockBits(bmpData);
return bmp;
}
return null;
}
It looks to me like you are always trying to copy the number of bytes myByteArray.Length to the bitmap buffer.
You are not checking that the bitmap buffer is in fact as big as that - so are probably writing off the end of the bitmap buffer.
Try checking if myByteArray.Length is ever greater than bmpData.Stride x bmp.Height
If this is the case you'll need to relook at the assumptions you've made with your hard coded values for width, height and pixel format.
You shouldn't copy the entire image at once. The memory allocation of the bitmap object might not be what you expect. For example the first scan line may be stored last in memory, which would mean that the data for the second scan line would end up outside the allocated memory area for the bitmap object. Also there may be padding between the scan lines to place them on an even address.
Copy one line at a time, using bmpData.Stride to find the next scan line:
int offset = 0;
long ptr = bmpData.Scan0.ToInt64();
for (int i = 0; i < Height; i++) {
Marshal.Copy(myByteArray, offset, new IntPtr(ptr), Width * 3);
offset += Width * 3;
ptr += bmpData.Stride;
}
I've personally seen some heap corruptions (debugging the crash dumps) because .Length was used.
As in:
IntPtr ptr = bitmapdata.Scan0;
Marshal.Copy(pixeldata, 0, ptr, pixeldata.Length);
The solution to the heap corruption was to do calculate the .Length differently:
IntPtr ptr = bitmapdata.Scan0;
int bytes = Math.Abs(bitmapdata.Stride) * bmp.Height;
Marshal.Copy(pixeldata, 0, ptr, bytes);
bytes and .Length had 1 byte difference, resulting in the heap corruption.
Math.Abs was taken directly from the example of Microsoft.
Because the Stride can be negative for a bottom-up bitmap.
Example microsoft: https://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.scan0%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396#Examples
(+ don't forget the .Unlock and add it in a try-finally statement.)
Answer for me: forgot to ->
// Unlock the bits right after Marshal.Copy
bmp.UnlockBits(bmpData);
Has anyone figured this out? This is about the fourth page without an answer. Using the exact code from msdn: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx which is:
Bitmap bmp = new Bitmap("c:\\picture.jpg");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
//This causes read or write protected memory
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
This doesn't work in optimize mode and running as exe not in IDE. Any ideas
I tried to put this is a new project and if I attached to process when i push a button, and press this button multiple times the error occurs, but in my code I'm only calling once, either way not sure why the error.
Maybe
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
has invalid argument for write only - try ReadWrite, or ReadOnly in ImageLockMode?
Maybe that helps.
I was searching a bit and if we skip the possibility that the array is not of the proper size, we end in Remarks for BitmapData.Stride Property:
The stride is the width of a single row of pixels (a scan line),
rounded up to a four-byte boundary. If the stride is positive, the
bitmap is top-down. If the stride is negative, the bitmap is
bottom-up.
So, maybe we should do it this way:
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(myByteArray, 0, bmpData.Scan0 +
( bmpData.Stride >= 0 ? 0 : bmpData.Stride*(bmp.Height-1) ),
myByteArray.Length);
But I wondered: We have created the bitmap: new Bitmap(Width, Height, PixelFormat.Format24bppRgb); ...so, how could it possibly be negative?
Time for ILSpy:
public Bitmap(int width, int height, PixelFormat format)
{
IntPtr zero = IntPtr.Zero;
int num = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, 0, (int)format, NativeMethods.NullHandleRef, out zero);
if (num != 0)
{
throw SafeNativeMethods.Gdip.StatusException(num);
}
base.SetNativeImage(zero);
}
// System.Drawing.SafeNativeMethods.Gdip
[DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
internal static extern int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, HandleRef scan0, out IntPtr bitmap);
And it pointed me here and here:
Bitmap(
[in] INT width,
[in] INT height,
[in] INT stride,
[in] PixelFormat format,
[in] BYTE *scan0
);
stride [in]
Type: INT
Integer that specifies the byte offset between the beginning of one scan line and the next. This is usually (but not necessarily) the
number of bytes in the pixel format (for example, 2 for 16 bits per
pixel) multiplied by the width of the bitmap. The value passed to this
parameter must be a multiple of four.
What does it mean to pass 0? Don't know, couldn't find it. Can anybody? Can the bitmap be created with negative stride? (by .NET new Bitmap(Width, Height, PixelFormat.Format24bppRgb)). In any case, we need to at least check BitmapData.Stride.
I found the rawStride formula in the code example for the BitmapSource Class. It seems worth trying creating an array using the code below and attempting to execute your copy method multiple times without it bombing. If you can there is a fair chance that this is an array sizing problem. If the data from your camera doesn't match the size of the bitmap in memory, you'll probably have to copy the data line by line.
private byte[] CreateImageByteArray(int width, int height, PixelFormat pixelFormat)
{
int rawStride = (width * pixelFormat.BitsPerPixel + 7) / 8;
byte[] rawImage = new byte[rawStride * height];
return rawImage;
}
The other thing you should do is make sure that Bitmap object is being Disposed properly when you finish with it. I have occasionally seen weird results with objects that have been operated on with unmanaged code and not cleaned up afterwards.
Also, transiting objects across threads can be tricky sometimes. See How to: Make Thread-Safe Calls to Windows Forms Controls and Thread-Safe Calls Using Windows Form Controls in C# article.
Lets try to change ThreadApartmentState to Single Threaded.
Also, check for cross-thread operations causing this errors.
It happens to me...
bmpData.Scan0 point to the first line of the bitmap and
bmpData.Stride is the number of bytes of a line. It is most of the time a negative number.
it means that the second line of the bitmap is at the address
bmpData.Scan0 - MAth.abs(bmpData.Stride)
the n line of the bitmap is at the address
bmpData.Scan0 - n * Math.Abs(bmpData.Stride)
so if you copy from bmpData.Scan0 more than one line , you have an exception