I have tried to dispose and use the 'using' statement to prevent high memory usage. However, it perplexes me when my memory doesn't clear everything.
Below is the method where I save the bitmap pictures into an Excel worksheet.
I ran 2 simulations
I deleted the bitmap list before it comes in here and it uses 200mb+.
I loaded the bitmap list and it uses 600+mb (normal) before this method. After going into loop in this method, it adds another 600mb, totaling to 1.2GB. After exiting this method, it goes down to 600mb+. What am i missing out because I feel that the memory should be around 200mb - 300mb only.
In the code, I used 2 'using' statements to enable auto dispose of the image and the stream.
Thank you for helping!
private void SaveBitMapIntoExcelSht(ref List<Bitmap> bmpLst, IXLWorksheet wksheet, int pixH)
{
string inputCell;
using (MemoryStream stream = new MemoryStream())
{
for (int i = 0; i < bmpLst.Count; i++)
{
Console.WriteLine(GC.GetTotalMemory(true));
inputCell = "A" + (30).ToString();
using (Image image = Image.FromHbitmap(bmpLst[i].GetHbitmap()))//Convert bitmap to hbitmap and store as a stream to save directly into the excel file.
{
Console.WriteLine(GC.GetTotalMemory(true));
// Save image to stream.
image.Save(stream, ImageFormat.Png);
IXLPicture pic = wksheet.AddPicture(stream, XLPictureFormat.Png);
pic.MoveTo(wksheet.Cell(inputCell));
pic.Delete();
pic.Dispose();
image.Dispose();
GC.Collect();
}
}
}
foreach (var bmp in bmpLst)
{
bmp.Dispose();
}
bmpLst.Clear();
Dispose();
GC.Collect();
Console.WriteLine(GC.GetTotalMemory(true));
}
EDIT: ANSWER
For those who are interested, you may find the code below that works.
Previously I got the hbitmap in the using statement but there wasnt a reference to it, therefore I created a var handle so that I can delete it.
Add this in your class
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
using (MemoryStream stream = new MemoryStream())
{
for (int i = 0; i < bmpLst.Count; i++)
{
Console.WriteLine(GC.GetTotalMemory(true));
inputCell = "A" + (i * numberOfCells + 1).ToString();
//using (Image image = Image.FromHbitmap(bmpLst[i].GetHbitmap()))
var handle = bmpLst[i].GetHbitmap();
using (Image image = Image.FromHbitmap(handle))//Convert bitmap to hbitmap and store as a stream to save directly into the excel file.
{
// Save image to stream.
image.Save(stream, ImageFormat.Png);
pic = wksheet.AddPicture(stream, XLPictureFormat.Png);
pic.MoveTo(wksheet.Cell(inputCell));
try
{
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(handle);
}
}
}
}
From the documentation of GetHBitmap
You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object. For more information about GDI bitmaps, see Bitmaps in the Windows GDI documentation.
and the documentation of FromHbitmap
The FromHbitmap method makes a copy of the GDI bitmap; so you can release the incoming GDI bitmap using the GDI DeleteObject method immediately after creating the new Image.
I.e. you are creating a GDI object you are never removing. So you need to call the pInvoke function DeleteObject on the IntPtr
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DeleteObject([In] IntPtr hObject);
Or use some other way to convert a bitmap to an image.
As a rule of thumb, if you ever see anything returning IntPtr it may represent some unmanaged resource, and if so you need to be extra careful to check if you need to manually dispose/delete something.
Another rule of thumb is to use a memory profiler if you suspect a memory leak. It might not have found this issue since it involves pointers to unmanaged memory, but it should be the first step to investigate.
Related
I have the Image of a PictureBox pointing to a certain file "A". At execution time I want to change the Image of the PictureBox to a different one "B" but I get the following error:
"A first chance exception of type 'System.IO.IOException' occurred in mscorlib.dll
Additional information: The process cannot access the file "A" because it is being used by another process."
I'm setting the Image as follows:
pbAvatar.Image = new Bitmap(filePath);
How can I unlock the first file?
Here is my approach to opening an image without locking the file...
public static Image FromFile(string path)
{
var bytes = File.ReadAllBytes(path);
var ms = new MemoryStream(bytes);
var img = Image.FromStream(ms);
return img;
}
UPDATE: I did some perf tests to see which method was the fastest. I compared it to #net_progs "copy from bitmap" answer (which seems to be the closest to correct, though does have some issues). I loaded the image 10000 times for each method and calculated the average time per image. Here are the results:
Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.
The results seem to make sense since you have to create the image twice using the copy from bitmap method.
UPDATE:
if you need a BitMap you can do:
return (Bitmap)Image.FromStream(ms);
This is a common locking question widely discussed over the web.
The suggested trick with stream will not work, actually it works initially, but causes problems later. For example, it will load the image and the file will remain unlocked, but if you try to save the loaded image via Save() method, it will throw a generic GDI+ exception.
Next, the way with per pixel replication doesn't seem to be solid, at least it is noisy.
What I found working is described here: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile--locks-file.aspx
This is how the image should be loaded:
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}
I was looking for a solution to this problem and this method works fine for me so far, so I decided to describe it, since I found that many people advise the incorrect stream approach here and over the web.
Using a filestream will unlock the file once it has been read from and disposed:
using (var fs = new System.IO.FileStream("c:\\path to file.bmp", System.IO.FileMode.Open))
{
var bmp = new Bitmap(fs);
pct.Image = (Bitmap) bmp.Clone();
}
Edit: Updated to allow the original bitmap to be disposed, and allow the FileStream to be closed.
THIS ANSWER IS NOT SAFE - See comments, and see discussion in net_prog's answer. The Edit to use Clone does not make it any safer - Clone clones all fields, including the filestream reference, which in certain circumstances will cause a problem.
You can't dispose / close a stream while a bitmap object is still using it. (Whether the bitmap object will need access to it again is only deterministic if you know what type of file you are working with and exactly what operations you will be performing. -- for example for SOME .gif format images, the stream is closed before the constructor returns.)
Clone creates an "exact copy" of the bitmap (per documentation; ILSpy shows it calling native methods, so it's too much to track down right now) likely, it copies that Stream data as well -- or else it wouldn't be an exact copy.
Your best bet is creating a pixel-perfect replica of the image -- though YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data as well.) But for most images, this works:
static Bitmap LoadImage(Stream stream)
{
Bitmap retval = null;
using (Bitmap b = new Bitmap(stream))
{
retval = new Bitmap(b.Width, b.Height, b.PixelFormat);
using (Graphics g = Graphics.FromImage(retval))
{
g.DrawImage(b, Point.Empty);
g.Flush();
}
}
return retval;
}
And then you can invoke it like such:
using (Stream s = ...)
{
Bitmap x = LoadImage(s);
}
As far as I know, this is 100% safe, since the resulting image is 100% created in memory, without any linked resources, and with no open streams left behind in memory. It acts like any other Bitmap that's created from a constructor that doesn't specify any input sources, and unlike some of the other answers here, it preserves the original pixel format, meaning it can be used on indexed formats.
Based on this answer, but with extra fixes and without external library import.
/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from http://stackoverflow.com/a/3661892/ with some extra fixes.
/// </summary>
/// <param name="sourceImage">The image to clone</param>
/// <returns>The cloned image</returns>
public static Bitmap CloneImage(Bitmap sourceImage)
{
Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
Bitmap targetImage = new Bitmap(rect.Width, rect.Height, sourceImage.PixelFormat);
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
BitmapData sourceData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.WriteOnly, targetImage.PixelFormat);
Int32 actualDataWidth = ((Image.GetPixelFormatSize(sourceImage.PixelFormat) * rect.Width) + 7) / 8;
Int32 h = sourceImage.Height;
Int32 origStride = sourceData.Stride;
Boolean isFlipped = origStride < 0;
origStride = Math.Abs(origStride); // Fix for negative stride in BMP format.
Int32 targetStride = targetData.Stride;
Byte[] imageData = new Byte[actualDataWidth];
IntPtr sourcePos = sourceData.Scan0;
IntPtr destPos = targetData.Scan0;
// Copy line by line, skipping by stride but copying actual data width
for (Int32 y = 0; y < h; y++)
{
Marshal.Copy(sourcePos, imageData, 0, actualDataWidth);
Marshal.Copy(imageData, 0, destPos, actualDataWidth);
sourcePos = new IntPtr(sourcePos.ToInt64() + origStride);
destPos = new IntPtr(destPos.ToInt64() + targetStride);
}
targetImage.UnlockBits(targetData);
sourceImage.UnlockBits(sourceData);
// Fix for negative stride on BMP format.
if (isFlipped)
targetImage.RotateFlip(RotateFlipType.Rotate180FlipX);
// For indexed images, restore the palette. This is not linking to a referenced
// object in the original image; the getter of Palette creates a new object when called.
if ((sourceImage.PixelFormat & PixelFormat.Indexed) != 0)
targetImage.Palette = sourceImage.Palette;
// Restore DPI settings
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
return targetImage;
}
To call, simply use:
/// <summary>Loads an image without locking the underlying file.</summary>
/// <param name="path">Path of the image to load</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(String path)
{
using (Bitmap sourceImage = new Bitmap(path))
{
return CloneImage(sourceImage);
}
}
Or, from bytes:
/// <summary>Loads an image from bytes without leaving open a MemoryStream.</summary>
/// <param name="fileData">Byte array containing the image to load.</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(Byte[] fileData)
{
using (MemoryStream stream = new MemoryStream(fileData))
using (Bitmap sourceImage = new Bitmap(stream)) {
{
return CloneImage(sourceImage);
}
}
Here's the technique I'm currently using, and seems to work best. It has the advantage of producing a Bitmap object with the same pixel format (24-bit or 32-bit) and resolution (72 dpi, 96 dpi, whatever) as the source file.
// ImageConverter object used to convert JPEG byte arrays into Image objects. This is static
// and only gets instantiated once.
private static readonly ImageConverter _imageConverter = new ImageConverter();
This can be used as often as needed, as follows:
Bitmap newBitmap = (Bitmap)_imageConverter.ConvertFrom(File.ReadAllBytes(fileName));
Edit:
Here's an update of the above technique: https://stackoverflow.com/a/16576471/253938
(The accepted answer is wrong. When you try to LockBits(...) on the cloned bitmap eventually you will encounter GDI+ errors.)
I see only 3 ways to get out of this:
copy your file to a temporary file and open that the easy way new Bitmap(temp_filename)
open your file, read image, create a pixel-size-pixelformat copy (don't Clone()) and dispose the first bitmap
(accept the locked-file-feature)
I suggest to use PixelMap (available on NuGet)
or Github
Very easy to use and much faster than standard Bitmap from .NET
PixelMap pixelMap = new PixelMap(bild);
pictureBox1.Image = pixelMap.GetBitmap();
Read it into the stream, create bitmap, close the stream.
I have a simple application, which renders images and prints them out. In order to make it easy and convenient to design, I have used custom Control.
Now, I have a simple control of size 1800 x 2400 (yeah, pretty big):
public class PhotoControl : UserControl
{
public PhotoControl()
{
InitializeComponent();
}
}
And an extension class, which uses it for generating images:
public static class PhotoProcessor
{
private static readonly PhotoControl PhotoForm = new PhotoControl(); // Single instance
public static Image GenerateImage(this Photo photo)
{
PhotoForm.SetPhoto(photo); // Apply a photo
Image result;
using (Bitmap b = new Bitmap(PhotoForm.Width, PhotoForm.Height))
{
Rectangle r = new Rectangle(0, 0, b.Width, b.Height);
PhotoForm.DrawToBitmap(b, r); // Draw control to bitmap
using (MemoryStream ms = new MemoryStream())
{
b.Save(ms, ImageFormat.Png); // Save bitmap as PNG
result = Image.FromStream(ms);
}
}
// GC.Collect();
// GC.WaitForPendingFinalizers();
return result; // return
}
Now, I try to generate 30 photos using this:
myPhoto.GenerateImage().Save(#"Output1.png", ImageFormat.Png);
myPhoto.GenerateImage().Save(#"Output2.png", ImageFormat.Png);
I do not store the reference, I just save an image and expect that GC will collect these images after saving to a file.
It will take about 2 GB of memory, and finally throw an exception:
An unhandled exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll
Additional information: Out of memory.
If I look at Visual Studio diagnostic tools, it looks like:
Let's take a snapshot, and look at the contents of the heap, we will see that there are many MemoryStreams:
What causes MemoryStream to produce a memory leak? As far as I know, using() generates Dispose() call, which should take care of it.
P.S. If I remove comment and call GC.Collect(); GC.WaitForPendingFinalizers();, then it takes 2-3 times less memory, but it still grows - ~200 images will still kill an application.
Changing the image saving to the following:
Image img;
img = myPhoto.GenerateImage();
img.Save(#"Output1.png", ImageFormat.Png);
img.Dispose();
img = myPhoto.GenerateImage();
img.Save(#"Output2.png", ImageFormat.Png);
img.Dispose();
will produce the same result as using GC.Collect();. It will also take less memory, but not eliminate memory leakage.
Image implements IDisposable and thus must be disposed manually. You're currently returning an Image from GenerateImage but not disposing it after saving.
I have a ushort[] containing image data I need to display on screen, at the minute I am creating a Windows.System.Drawing.Bitmap, and converting this to a BitmapImage but this feels like a slow inneficent way to do this.
Does anyone what the fastest way to create a BitmapImage of a ushort[] is?
or alternativly create an ImageSource object from the data?
Thanks,
Eamonn
My previous method for converting Bitmap to BitmapImage was:
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Png);
ms.Position = 0;
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
I was able to speed it up using
Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
EDIT:
anyone using this should know that bitmap.GetHbitmap creates an unmanaged object lying around, since this is unmanaged it wont be picked up by the .net garbage collector and must be deleted to avoid a memory leak, use the following code to solve this:
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
IntPtr hBitmap = bitmap.GetHbitmap();
try
{
imageSource = Imaging.CreateBitmapSourceFromHBitmap(hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
catch (Exception e) { }
finally
{
DeleteObject(hBitmap);
}
(its not very neat having to import a dll like like but this was taken from msdn, and seems to be the only way around this issue - http://msdn.microsoft.com/en-us/library/1dz311e4.aspx )
I have an application that works with Enhanced Metafiles.
I am able to create them, save them to disk as .emf and load them again no problem.
I do this by using the gdi32.dll methods and the DLLImport attribute.
However, to enable Version Tolerant Serialization I want to save the metafile in an object along with other data.
This essentially means that I need to serialize the metafile data as a byte array and then deserialize it again in order to reconstruct the metafile.
The problem I have is that the deserialized data would appear to be corrupted in some way, since the method that I use to reconstruct the Metafile raises a "Parameter not valid exception".
At the very least the pixel format and resolutions have changed.
Code use is below.
[DllImport("gdi32.dll")]
public static extern uint GetEnhMetaFileBits(IntPtr hemf, uint cbBuffer, byte[] lpbBuffer);
[DllImport("gdi32.dll")]
public static extern IntPtr SetEnhMetaFileBits(uint cbBuffer, byte[] lpBuffer);
[DllImport("gdi32.dll")]
public static extern bool DeleteEnhMetaFile(IntPtr hemf);
The application creates a metafile image and passes it to the method below.
private byte[] ConvertMetaFileToByteArray(Image image)
{
byte[] dataArray = null;
Metafile mf = (Metafile)image;
IntPtr enhMetafileHandle = mf.GetHenhmetafile();
uint bufferSize = GetEnhMetaFileBits(enhMetafileHandle, 0, null);
if (enhMetafileHandle != IntPtr.Zero)
{
dataArray = new byte[bufferSize];
GetEnhMetaFileBits(enhMetafileHandle, bufferSize, dataArray);
}
DeleteEnhMetaFile(enhMetafileHandle);
return dataArray;
}
At this point the dataArray is inserted into an object and serialized using a BinaryFormatter.
The saved file is then deserialized again using a BinaryFormatter and the dataArray retrieved from the object.
The dataArray is then used to reconstruct the original Metafile using the following method.
public static Image ConvertByteArrayToMetafile(byte[] data)
{
Metafile mf = null;
try
{
IntPtr hemf = SetEnhMetaFileBits((uint)data.Length, data);
mf = new Metafile(hemf, true);
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
return (Image)mf;
}
The reconstructed metafile is then saved saved to disk as a .emf (Model) at which point it can be accessed by the Presenter for display.
private static void SaveFile(Image image, String filepath)
{
try
{
byte[] buffer = ConvertMetafileToByteArray(image);
File.WriteAllBytes(filepath, buffer); //will overwrite file if it exists
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
The problem is that the save to disk fails. If this same method is used to save the original Metafile before it is serialized everything is OK. So something is happening to the data during serialization/deserializtion.
Indeed if I check the Metafile properties in the debugger I can see that the ImageFlags, PropertyID, resolution and pixelformats change.
Original Format32bppRgb changes to Format32bppArgb
Original Resolution 81 changes to 96
I've trawled though google and SO and this has helped me get this far but Im now stuck.
Does any one have enough experience with Metafiles / serialization to help..?
EDIT: If I serialize/deserialize the byte array directly (without embedding in another object) I get the same problem.
Consider this code for loading, modifying and saving a Bitmap image:
using (Bitmap bmp = new Bitmap("C:\\test.jpg"))
{
bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
bmp.Save("C:\\test.jpg");
}
it runs without any exception.
But consider this one:
using (Bitmap bmp = new Bitmap("C:\\test.jpg"))
{
using (Bitmap bmpClone = (Bitmap)bmp.Clone())
{
//You can replace "bmpClone" in the following lines with "bmp",
//exception occurs anyway
bmpClone.RotateFlip(RotateFlipType.Rotate180FlipNone);
bmpClone.Save("C:\\test.jpg");
}
}
It ends in an ExternalException with this message: "A generic error occurred in GDI+".
What's wrong here? Any kind of lock on opened file? If so, why the first block works? What is the proper code for cloning a System.Drawing.Bitmap while we may need to edit main object or its clone in the memory and still have them both loaded in memory?
Yes, the file is locked when the first bitmap object is loaded and thus bmpClone.Save() to the same file fails because you have a logical deadlock.
When opening Bitmaps by filename, the file is locked throughout the life of the Bitmap. If you use a stream, the stream must remain open.
Update:
If you wish to have two bitmaps in memory for use outside of the scope of the method you are in, then you won't be using a using block.
Create the first image from file, and then clone it. Utilize them as needed throughout your UI lifecycle but make sure you clean them up using Dispose() when they are no longer needed so that underlying resources are released.
Also, from MSDN:
Saving the image to the same file it
was constructed from is not allowed
and throws an exception
That's pretty awkward. If the object created using clone() keeps information on the image source (e.g. a handle on the original file) or you can't otherwise unlock the file while using the Bitmap instances, then you'll probably have to either save to a new file, or open from a temporary copy of the original.
Try this instead:
// ... make a copy of test.jpg called test_temp.jpg
Bitmap bmpOriginal = new Bitmap("C:\\test_temp.jpg"))
Bitmap bmpClone = (Bitmap)bmp.Clone();
// ... do stuff
bmpClone.RotateFlip(RotateFlipType.Rotate180FlipNone);
bmpClone.Save("C:\\test.jpg");
// ... cleanup
bmpOriginal.Dispose();
bmpClone.Dispose();
This is how I copy Bitmaps:
[DllImport("kernel32.dll", EntryPoint = "CopyMemory")]
static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);
public static Bitmap KernellDllCopyBitmap(Bitmap bmp, bool CopyPalette = true)
{
Bitmap bmpDest = new Bitmap(bmp.Width, bmp.Height, bmp.PixelFormat);
if (!KernellDllCopyBitmap(bmp, bmpDest, CopyPalette))
bmpDest = null;
return bmpDest;
}
/// <summary>
/// Copy bitmap data.
/// Note: bitmaps must have same size and pixel format.
/// </summary>
/// <param name="bmpSrc">Source Bitmap</param>
/// <param name="bmpDest">Destination Bitmap</param>
/// <param name="CopyPalette">Must copy Palette</param>
public static bool KernellDllCopyBitmap(Bitmap bmpSrc, Bitmap bmpDest, bool CopyPalette = false)
{
bool copyOk = false;
copyOk = CheckCompatibility(bmpSrc, bmpDest);
if (copyOk)
{
BitmapData bmpDataSrc;
BitmapData bmpDataDest;
//Lock Bitmap to get BitmapData
bmpDataSrc = bmpSrc.LockBits(new Rectangle(0, 0, bmpSrc.Width, bmpSrc.Height), ImageLockMode.ReadOnly, bmpSrc.PixelFormat);
bmpDataDest = bmpDest.LockBits(new Rectangle(0, 0, bmpDest.Width, bmpDest.Height), ImageLockMode.WriteOnly, bmpDest.PixelFormat);
int lenght = bmpDataSrc.Stride * bmpDataSrc.Height;
CopyMemory(bmpDataDest.Scan0, bmpDataSrc.Scan0, (uint)lenght);
bmpSrc.UnlockBits(bmpDataSrc);
bmpDest.UnlockBits(bmpDataDest);
if (CopyPalette && bmpSrc.Palette.Entries.Length > 0)
bmpDest.Palette = bmpSrc.Palette;
}
return copyOk;
}
public static bool CheckCompatibility(Bitmap bmp1, Bitmap bmp2)
{
return ((bmp1.Width == bmp2.Width) && (bmp1.Height == bmp2.Height) && (bmp1.PixelFormat == bmp2.PixelFormat));
}
## ImageCopyBenchmark ##
Image Size: {Width=1024, Height=1024}.
Image PixelFormat: Format8bppIndexed.
Bitmap.Clone(): 0,00 ms (Not a DeepCopy... Same pixel data - look here)
Bitmap.Clone() + RotateFlip (to guet a deep copy): 2,02 ms
KernellDllCopyBitmap: 0,52 ms (THE BEST!)
MarshalCopyBitmap: 2,21 ms
You could also load the bitmap without file locking using the simple workaround:
using (Stream s = File.OpenRead(#"\My Documents\My Pictures\Waterfall.jpg"))
Bitmap _backImage = (Bitmap)Bitmap.FromStream(s);
Here is my vanity method:
private static unsafe Bitmap DuplicateBitmap(Bitmap inputBitmap)
{
byte[] buffer = new byte[inputBitmap.Height * inputBitmap.Width *
Image.GetPixelFormatSize(inputBitmap.PixelFormat) / 8];
fixed (byte* p = buffer)
{
BitmapData b1Data = new BitmapData()
{
Scan0 = (IntPtr)p,
Height = inputBitmap.Height,
Width = inputBitmap.Width,
PixelFormat = inputBitmap.PixelFormat,
Stride = inputBitmap.Width * Image.GetPixelFormatSize(inputBitmap.PixelFormat) / 8,
};
inputBitmap.LockBits(new Rectangle(Point.Empty, inputBitmap.Size),
ImageLockMode.ReadOnly | ImageLockMode.UserInputBuffer, inputBitmap.PixelFormat, b1Data); // copy out.
Bitmap b2 = new Bitmap(b1Data.Width, b1Data.Height, b1Data.Stride, inputBitmap.PixelFormat, b1Data.Scan0);
inputBitmap.UnlockBits(b1Data);
return b2;
}
}
10% faster (depending on bitmap size...)