Write grayscale BMP to ZIP file from Memorystream - c#

I'm trying to test whether writing individual images or a bundle zipped is quicker. My approach is to create a random byte array of values between 0 and 255 (8-bit image) and form a Bitmap from it, writing repeatedly using Bitmap.Save. In this way I can set the PixelFormat to Format8bppIndexed, which gives a grayscale image:
// Random number Generator
Random rnd = new Random();
// Create a single image
int Width = 640;
int Height = 512;
var b = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
ColorPalette ncp = b.Palette;
for (int i = 0; i < 256; i++)
ncp.Entries[i] = Color.FromArgb(255, i, i, i);
b.Palette = ncp;
var BoundsRect = new Rectangle(0, 0, Width, Height);
BitmapData bmpData = b.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
b.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpData.Stride * b.Height;
var rgbValues = new byte[bytes];
// fill in rgbValues, e.g. with a for loop over an input array
rnd.NextBytes(rgbValues);
Marshal.Copy(rgbValues, 0, ptr, bytes);
b.UnlockBits(bmpData);
// copy image to a list of ~1000
List<Bitmap> bmps = new List<Bitmap>();
for (int i = 0; i < 500; i++)
bmps.Add(new Bitmap(b));
// Write to individual files
DateTime t0=DateTime.Now;
for (int i=0;i<bmps.Count;i++)
b.Save(#"C:\Temp\DiskTransferTest\IndividualImages\" + i.ToString() + ".bmp");
DateTime t1=DateTime.Now;
Console.WriteLine("Time to write individually: " + (t1-t0).ToString());
After that, I try and zip them all into a single ZIP file and save, using DotNetZip. This works, but I get a colour image rather than a greyscale one, so the filesizes are much larger.
// Create memorystreams from bitmap to pass to DotNetZip
List<MemoryStream> mss = new List<MemoryStream>();
for (int i = 0; i < bmps.Count; i++)
{
mss.Add(new MemoryStream());
bmps[i].Save(mss[i], ImageFormat.Bmp);
mss[i].Seek(0, SeekOrigin.Begin);
}
// Compress and write
t0 = DateTime.Now;
using (ZipFile zipfile = new ZipFile())
{
zipfile.CompressionLevel = 0;
int i=0;
foreach (MemoryStream ms in mss)
{
string pictureName = i.ToString() + ".bmp";
zipfile.AddEntry(pictureName,ms);
i++;
}
zipfile.Save(#"C:\Temp\DiskTransferTest\zipped.zip");
}
t1 = DateTime.Now;
Console.WriteLine("Time to write compressed: " + (t1 - t0).ToString());
Any suggestions on how to write a greyscale to the zip via the MemoryStream?

The problem is that your new bitmaps aren't 8bpp bitmaps. Consider your code:
// copy image to a list of ~1000
List<Bitmap> bmps = new List<Bitmap>();
for (int i = 0; i < 500; i++)
bmps.Add(new Bitmap(b));
// Write to individual files
DateTime t0=DateTime.Now;
for (int i=0;i<bmps.Count;i++)
b.Save(#"C:\Temp\DiskTransferTest\IndividualImages\" + i.ToString() + ".bmp");
The bitmap b is an 8bpp bitmap. You're writing it to file. But if you examine bmps[0] I think you'll find that the PixelFormat is 32bpp. At least, that's what happens when I execute this code:
var bmp = new Bitmap(640, 480, PixelFormat.Format8bppIndexed);
Console.WriteLine(bmp.PixelFormat); // 8 bpp
var bmp2 = new Bitmap(bmp);
Console.WriteLine(bmp2.PixelFormat); // 32 bpp
In your code that writes the bitmaps to the memory stream, you're accessing bmps[i], rather than the 8bpp image, b, as you are when writing to file.
You need to create your list bitmaps, set their properties, and then copy b. You can't duplicate the bitmaps with their properties with the new Bitmap(b) constructor call.

As far as I know, it's not possible to create a true grayscale image using the Bitmap class. You would have to exchange the Bitmap.Palette property from the present ColorPalette to a GrayscalePalette that would only store one byte per color instead of the four bytes needed per ARGB color. The framework does not contain any such class, ColorPalette does not inherit a base class or implement an interface, and it's also sealed so you can't inherit from the class either.
On the other hand, checking the bitmap file format specification I see that there is no way to save a true grayscale bitmap image (using a 256 byte color table). Saving an 8 bit grayscale image in Photoshop CS6 and then opening it again shows that it was saved as a 8 bit color indexed image (although R = G = B for all colors in the palette).

Related

Process Bitmap in C# using underlying buffer accessed with Bitmap.LockBits function

I need to do an analysis with an arbitrary image. I would like to start with the easiest example - just copy a image to picturebox.
Bitmap foreImg = new Bitmap("input.jpg");
//output image
Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height);
unsafe
{
BitmapData oneBits = foreImg.LockBits(new Rectangle(0, 0, foreImg.Width, foreImg.Height), ImageLockMode.ReadOnly, foreImg.PixelFormat);
BitmapData thrBits = resImg.LockBits(new Rectangle(0, 0, resImg.Width, resImg.Height), ImageLockMode.WriteOnly, resImg.PixelFormat);
System.Threading.Tasks.Parallel.For(0, foreImg.Width * foreImg.Height, j =>
{
Pixel* pxOne = (Pixel*)((byte*)oneBits.Scan0 + j * sizeof(Pixel));
Pixel* pxRes = (Pixel*)((byte*)thrBits.Scan0 + j * sizeof(Pixel));
pxRes->Green = pxOne->Green;
pxRes->Red = pxOne->Red;
pxRes->Blue = pxOne->Blue;
});
foreImg.UnlockBits(oneBits);
resImg.UnlockBits(thrBits);
}
In the result of my program the image is distorted
Original: original_image
After: after_image. What am I doing wrong?
Thanks! The problem was is that PixelFormat of input images does not match with my struct Pixel. Indeed, I wasn't add alpha byte, and in this case I was suppose to use Format24bppRgb.
Your code for image copy has couple errors due to assumptions which turn not true for particular image which is copied. First assumption us that when you create new target image for copy operation it will have exactly the same pixel representation as the source image what may be sometimes true but in many cases will not:
Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height);
should be instead:
Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height, foreImg.PixelFormat);
The next assumption which may or may not turn wrong depending on image is an implicit assumption that the source image PixelFormat is exactly 3 bytes in size and corresponds to PixelFormat.Format24bppRgb format (or multiple of 3 bytes as I do not know what is the size of Red, Green or Blue channel in your Pixel structure and it could be PixelFormat.Format48bppRgb format) and consequently the bytes are copied from the source image to the destination image based on this assumption.
To perform exact copy it is necessary to copy exactly the same number of bytes from source image to destination image and it does not require using an underlying Pixel structure but instead it can be based on integer copy. Last but not least if the goal is to copy image instead of analyzing it's content Pixel by Pixel the fastest method is to use specialized memory copy function:
System.Buffer.MemoryCopy((void*)oneBits.Scan0, (void*)thrBits.Scan0, byteLength, byteLength);
Below there is a code listing with code which copies an image using ulong as a carrier. I have added function which returns Pixel size in bytes which is used to calculate image size in bytes and perform exact copy. However it can be used to select matching Pixel structure which than can be used to analyze image data. For instance if an image has PixelFormat.Format24bppRgb format one can use Pixel structure of 3 byte size and RGB colors. For other formats it would be necessary to define other Pixel structures which would directly replicate image Pixel format.
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace DrawingImagingOperations
{
class Program
{
static void Main(string[] args)
{
Bitmap foreImg = new Bitmap(#"..\..\YaHI9.jpg");
//output image
Bitmap resImg = new Bitmap(foreImg.Width, foreImg.Height, foreImg.PixelFormat);
unsafe
{
BitmapData oneBits = foreImg.LockBits(new Rectangle(0, 0, foreImg.Width, foreImg.Height), ImageLockMode.ReadOnly, foreImg.PixelFormat);
BitmapData thrBits = resImg.LockBits(new Rectangle(0, 0, resImg.Width, resImg.Height), ImageLockMode.WriteOnly, resImg.PixelFormat);
int pixelSize = GetPixelSize(foreImg.PixelFormat);
var byteLength = foreImg.Width * foreImg.Height * pixelSize;
var length = byteLength / sizeof(UInt64);
var reminder = byteLength % sizeof(UInt64);
System.Threading.Tasks.Parallel.For(0, length, j =>
{
ulong* pxOne = (ulong*)((byte*)oneBits.Scan0 + j * sizeof(UInt64));
ulong* pxRes = (ulong*)((byte*)thrBits.Scan0 + j * sizeof(UInt64));
*pxRes = *pxOne;
});
if (reminder > 0)
{
byte* pSrc = (byte*)oneBits.Scan0 + (pixelSize * length);
byte* pDst = (byte*)thrBits.Scan0 + (pixelSize * length);
for (int j = length; j < byteLength; j++)
*pDst++ = *pSrc++;
}
foreImg.UnlockBits(oneBits);
resImg.UnlockBits(thrBits);
}
resImg.Save(#"..\..\imgCopy.jpg");
}
internal static int GetPixelSize(PixelFormat data)
{
switch (data)
{
case PixelFormat.Format8bppIndexed:
return 1;
case PixelFormat.Format16bppGrayScale:
case PixelFormat.Format16bppRgb555:
case PixelFormat.Format16bppRgb565:
case PixelFormat.Format16bppArgb1555:
return 2;
case PixelFormat.Format24bppRgb:
return 3;
case PixelFormat.Canonical:
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
case PixelFormat.Format32bppRgb:
return 4;
case PixelFormat.Format48bppRgb:
return 6;
case PixelFormat.Format64bppArgb:
case PixelFormat.Format64bppPArgb:
return 8;
}
throw new FormatException("Unsupported image format: " + data);
}
}
}

Is it possible to achieve the actual binary numbers from a bitmap?

My professor kind of "challenged me" to create an application that draws pixel by pixel an image converted in Bitmap, where it's data is saved in some sort of binary that I can't wrap my head around.
Here's the example given to me:
const byte image[]={
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,B00000000,
ect ect ect
Now, if the byte data type saves numbers that go from 0 to 255, how is this possible? In the sample code that I was given, there is also the use of "Word" data type but in my IDE it seems like it doesn't exist.
I already wrote the code that converts any image given in input into a bitmap:
FileStream fs = new FileStream(openFileDialog1.FileName, FileMode.Open, FileAccess.Read); //Path is image location
Byte[] bindata = new byte[Convert.ToInt32(fs.Length)];
fs.Read(bindata, 0, Convert.ToInt32(fs.Length));
Bitmap bmp;
using (var ms = new MemoryStream(bindata))
{
bmp = new Bitmap(ms);
}
pictureBox1.Image = bmp; //For now, I just display the converted image on screen
Now I suppose that the next step is to draw the image byte per byte, but I can't get my head around this binary thing and the word data type.. Any kind of help is appreciated :)
if you just want to draw a bitmap pixel at a time, you can do something like this:
Bitmap b = new Bitmap(10, 10);
b.SetPixel(0, 0, Color.Black);
b.SetPixel(1, 3, Color.Red);
pictureBox1.Image = b;
You can just copy your bytes to the Bitmap's memory buffer itself.
BitmapData bufferData = buffer.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
bufferData.SetPixel(x, y, CELL_DEAD);
buffer.UnlockBits(bufferData);
//////////
public static unsafe void SetPixel(BitmapData data, int x, int y, byte pixel)
{
*((byte*)data.Scan0 + y * data.Stride + x) = pixel;
}
I've used it as unsafe but you can play your magic with IntPtr. Of course, you must play your own with width-height synchronization.
UPD: set PixelFormat with care. PixelFormat.Format8bppIndexed is what you need if your colors are in default 256-color palette or you want to define your own palette.

BitLock-GetPixels. Time and PixelFormat

Okay, I've created two programs. One that uses GetPixels and another that uses LockBits. My GetPixels program is as follows...
The stripe picture referred is a 200x200 jpg
Stopwatch GetTime = new Stopwatch();
Bitmap img = new Bitmap("stripe.jpg");
GetTime.Start();
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
Color pixel = img.GetPixel(i, j);
output += " " + pixel;
}
}
GetTime.Stop();
Now this one reads out about 20 secs to process this image and output all the pixels. Great, but my LockBits one should, theoretically be faster. My code for the LockBits is...
Bitmap bmp = new Bitmap("stripe.jpg");
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height); //Creates Rectangle for holding picture
BitmapData bmpData = bmp.LockBits(bmpRec, ImageLockMode.ReadWrite, Pixels); //Gets the Bitmap data
IntPtr Pointer = bmpData.Scan0; //Scans the first line of data
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height; //Gets array size
byte[] rgbValues = new byte[DataBytes]; //Creates array
string Pix = " ";
Marshal.Copy(Pointer, rgbValues, 0, DataBytes); //Copies of out memory
bmp.UnlockBits(bmpData);
Stopwatch Timer = new Stopwatch();
pictureBox1.Image = bmp;
Timer.Start();
for (int p = 0; p < DataBytes; p++)
{
Pix += " " + rgbValues[p];
}
Timer.Stop();
and the time on that is 37secs. Now I dont understand why my time is longer for the Lockbits than it is for the GetPixels.
Also my output files don't match up in terms of where they are listed. It is almost as if they are out of order.
This is a big problem to tackle so thank you all in advance for reading and trying to solve my problem.
You have a few problems that I can see. The biggest issue is that your image has a width of 200, but in memory, its stride is 600 (for me - probably similar for you). This means you are writing out a lot more data, because you don't ignore the 400 padding pixels per row.
Other issues:
You're timing string concatenation only. By the time you start your timer, the lockbits stuff has finished.
Your string concatenation would be faster using StringBuilder.
You are locking the bitmap for read/write access, when you only need read. No discernible effect on performance for me, with this image, but still might as well change it to ReadOnly.
Most of your comments are unnecessary at best (// creates array)- some are misleading (//Scans the first line of data - no, it returns a pointer to the data which has already been loaded).
The following code completes in only a few milliseconds on my machine.
Bitmap bmp = new Bitmap(#"d:\stripe.jpg");
//pictureBox1.Image = bmp;
Stopwatch Timer = new Stopwatch();
Rectangle bmpRec = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(
bmpRec, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
IntPtr Pointer = bmpData.Scan0;
int DataBytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[DataBytes];
Marshal.Copy(Pointer, rgbValues, 0, DataBytes);
bmp.UnlockBits(bmpData);
StringBuilder pix = new StringBuilder(" ");
Timer.Start();
for (int i = 0; i < bmpData.Width; i++)
{
for (int j = 0; j < bmpData.Height; j++)
{
// compute the proper offset into the array for these co-ords
var pixel = rgbValues[i + j*Math.Abs(bmpData.Stride)];
pix.Append(" ");
pix.Append(pixel);
}
}
Timer.Stop();
Console.WriteLine(Timer.Elapsed);

How to create a Byte array that contains a real Image?

Please see my code below.
I want to create a Byte array with data that I can convert into a real image. When I try to run this code I get an argumentException. What do I need to do in the For loop in order to create a legitimate Byte array that will hold data of an image? I don't want to use a real image and convert it to byte array, I want to create an image form random numbers.
Random Rnd = new Random();
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Byte[] ByteArray = new Byte[1000];
for (int i = 0; i < 1000; i++)
{
ByteArray[i] = Convert.ToByte(Rnd.Next(9));
}
ImageConverter Convertor = new ImageConverter();
BitmapImage image = (BitmapImage)Convertor.ConvertFrom(ByteArray);
MyImage.Source = image;
}
Notice please that I don't want to work with WinForms types or libraries like system.drawing / bitmap - I only want to use WPF technology.
This is the solution you are looking for, using only WPF technology.
Note that the constant value of 16 used in the stride parameter calculation comes directly from the fact that I am using a 16-bit pixel format.
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Random rnd = new Random();
Byte[] ByteArray = new Byte[(int)MyImage.Width * (int)MyImage.Height * 3];
rnd.NextBytes(ByteArray);
var image = BitmapSource.Create((int) MyImage.Width, (int) MyImage.Height, 72, 72,
PixelFormats.Bgr565, null, ByteArray, (4*((int)MyImage.Width * 16 + 31)/32));
MyImage.Source = image;
}
This just might do the trick for you:
private static Bitmap GenBitmap(int width, int height) {
int ch = 3; //number of channels (ie. assuming 24 bit RGB in this case)
Random rnd = new Random();
int imageByteSize = width * height * ch;
byte[] imageData = new byte[imageByteSize]; //your image data buffer
rnd.NextBytes(imageData); //Fill with random bytes;
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr pNative = bmData.Scan0;
Marshal.Copy(imageData, 0, pNative, imageByteSize);
bitmap.UnlockBits(bmData);
return bitmap;
}
I'm not sure how Converter.ConvertFrom works but I prefer to do my bitmaps the lower-level way with Bitmap.LockBits() and a little Marshal.Copy().
See this method:
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
static Bitmap CreateRandomBitmap(Size size)
{
// Create a new bitmap for the size requested.
var bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb);
// Lock the entire bitmap for write-only acccess.
var rect = new Rectangle(0, 0, size.Width, size.Height);
var bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
// Calculate the number of bytes required and allocate them.
var numberOfBytes = bitmapData.Stride * size.Height;
var bitmapBytes = new byte[numberOfBytes];
// Fill the bitmap bytes with random data.
var random = new Random();
for (int x = 0; x < size.Width; x++)
{
for (int y = 0; y < size.Height; y++)
{
// Get the index of the byte for this pixel (x/y).
var i = ((y * size.Width) + x) * 4; // 32bpp
// Generate the next random pixel color value.
var value = (byte)random.Next(9);
bitmapBytes[i] = value; // BLUE
bitmapBytes[i + 1] = value; // GREEN
bitmapBytes[i + 2] = value; // RED
bitmapBytes[i + 3] = 0xFF; // ALPHA
}
}
// Copy the randomized bits to the bitmap pointer.
var ptr = bitmapData.Scan0;
Marshal.Copy(bitmapBytes, 0, ptr, numberOfBytes);
// Unlock the bitmap, we're all done.
bitmap.UnlockBits(bitmapData);
return bitmap;
}
Then you can do something like this:
public void Run()
{
using(var bitmap = CreateRandomBitmap(new Size(64, 64)))
{
bitmap.Save("random.png", ImageFormat.Png);
}
}
You can't use random bytes to create an image, because each type of image (bmp, jpeg, png) is constructed with a certain format. The code wouldn't know how to interpret random bytes.
http://en.wikipedia.org/wiki/Image_file_formats

reading a jpeg file in c#

I have a doubt in c#. How to read a jpeg or bmp file using c#? and how to store the pixel's RGB values in array? Then how to check whether the value is already exist or not?
James Schek has it, but beware that GetPixel is extremely, incredibly slow.
Here's a complete sample using lockbits:
/*Note unsafe keyword*/
public unsafe Image ThresholdUA(float thresh)
{
Bitmap b = new Bitmap(_image);//note this has several overloads, including a path to an image
BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);
byte bitsPerPixel = GetBitsPerPixel(bData.PixelFormat);
/*This time we convert the IntPtr to a ptr*/
byte* scan0 = (byte*)bData.Scan0.ToPointer();
for (int i = 0; i < bData.Height; ++i)
{
for (int j = 0; j < bData.Width; ++j)
{
byte* data = scan0 + i * bData.Stride + j * bitsPerPixel / 8;
//data is a pointer to the first byte of the 3-byte color data
}
}
b.UnlockBits(bData);
return b;
}
There's another way to do it using marshaling though. Here's the same thing, but with marshaling:
/*No unsafe keyword!*/
public Image ThresholdMA(float thresh)
{
Bitmap b = new Bitmap(_image);
BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);
/* GetBitsPerPixel just does a switch on the PixelFormat and returns the number */
byte bitsPerPixel = GetBitsPerPixel(bData.PixelFormat);
/*the size of the image in bytes */
int size = bData.Stride * bData.Height;
/*Allocate buffer for image*/
byte[] data = new byte[size];
/*This overload copies data of /size/ into /data/ from location specified (/Scan0/)*/
System.Runtime.InteropServices.Marshal.Copy(bData.Scan0, data, 0, size);
for (int i = 0; i < size; i += bitsPerPixel / 8 )
{
double magnitude = 1/3d*(data[i] +data[i + 1] +data[i + 2]);
//data[i] is the first of 3 bytes of color
}
/* This override copies the data back into the location specified */
System.Runtime.InteropServices.Marshal.Copy(data, 0, bData.Scan0, data.Length);
b.UnlockBits(bData);
return b;
}
Read the file using the Bitmap class.
Lock pixels.
Retrieve bytes from array.
Alternatively, you can use GetPixel if you just need one or two.
You can use Image.FromFile (http://msdn.microsoft.com/en-us/library/system.drawing.image.fromfile.aspx) to create an Image object from an image on disk.
As it was already mentioned, the fastest way to retrieve pixels is to use LockBits(), however, there's a way to do it without Marshal.Copy or unsafe code.
First, you'll need to compute Stride of your image:
var stride = ComputeStride(img.Width, format);
it is width*bytesPerPixel value rounded up to be divisible by 4. See formulas here.
Then you'll need to initialize an array of the required size:
var pixels = new byte[img.Height*stride]
Then you'll need to retrieve an unmanaged pointer to the beginning of this array.
You may use Marshal.UnsafeAddrOfPinnedArrayElement(pixels, 0), but it's safer to pin the array in memory:
var handle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
var scan0 = pixels.AddrOfPinnedObject();
You'll need to create BitmapData structure:
var bData = new BitmapData{Width = img.Width, height = img.Height, Stride = stride, Scan0 = scan0};
Then you'll pass it to LockBits method while setting ImageLockMode.UserInputBuffer flag.
img.LockBits(area, ImageLockMode.Readonly | ImageLockMode.UserInputBuffer, format, bData);
Voila! Pixels are stored in pixels array. But you'll need to unpin your buffer:
handle.Free();
This may seem cumbersome, but this is the fastest way, since only one copying of data is required.

Categories