I load a bitmap from a DataStream. I try to return the loaded bitmap from a method, and later use it as a source to clone a new bitmap. Unfortunately, the Clone() call results in an OutOfMemoryException. Through testing I realized that Clone() succeeds until the underlying data stream is disposed.
How can I create a bitmap that exists independent from the stream it was loaded from?
DxScreenCapture cap = new DxScreenCapture();
var surface = cap.CaptureScreen();
Bitmap png;
Rectangle rect;
PixelFormat fmt;
using (DataStream stream = Surface.ToStream(surface, ImageFileFormat.Bmp))
{
png = new Bitmap(stream);
fmt = png.PixelFormat;
rect = new Rectangle(911, 170, 32, 14);
// Works
Bitmap rgn1 = png.Clone(rect, fmt);
}
// Throws OutOfMemoryException
Bitmap rgn2 = png.Clone(rect, fmt);
Building on Hans' comment, I simply created a new bitmap from the original one.
Bitmap copy;
using (DataStream stream = Surface.ToStream(surface, ImageFileFormat.Bmp))
{
Bitmap orig = new Bitmap(stream);
copy = new Bitmap(orig);
}
// Able to use copy after stream is disposed
Related
I have the following code trying to compress a bitmap and use it for uploading.
Bitmap bitmap1 = BitmapFactory.DecodeByteArray(imageFileByte, 0, imageFileByte.Length);
byte[] compressedData = null;
using (var stream = new MemoryStream())
{
bitmap1.Compress(Bitmap.CompressFormat.Jpeg, 50, stream);
compressedData = stream.ToArray();
}
//load compressed bitmap from memory
using (var anotherStream = new MemoryStream(compressedData))
{
var compressedBitmap = BitmapFactory.DecodeStream(anotherStream);
}
When I debug, bitmap1 on line 1 shows 3021330 bytes, when come to the compressedData on line 6, it did shrink to 70128 bytes.
Now I want to convert again the compressedData to a bitmap again since it is a byte array but not a bitmap, when it comes to line 11, the compressedBitmap shows exactly same number of bytes with the bitmap1.
How can I actually use the bitmap that has already been compressed?
Is it possible to compress a bitmap and save the compressed one as a bitmap again?
Thanks for any comment.
I am using WCF for communication between client and server program on the same machine. Earlier I used Bitmaps to transfer video frames to the client. It was successful with WCF, but I only have 6 megabytes / sec LAN. Transferring 1.2Mb every second would block the LAN. I decided to JPEG encode the captured Bitmaps from the camera. It is possible to save a JPEG on the disk from a bitmap, or to a Memorystream. I created a
[MessageContract]
public class RemoteResponse
{
[MessageBodyMember(Order = 1)]
public System.IO.MemoryStream jpegImage;
}
I return this back from the (I)LiveImageService.
As I get it out in the client: RemoteResponse ret = client.GetLiveImage();
MemoryStream returnedImage = returnedResponse.jpegImage;
returnedImage.Position = 0L;
returnedImage.Seek(0, SeekOrigin.Begin);
args.image = returnedImage;
I do this in the event handler:
args.image.Position = 0L;
args.image.Seek(0, SeekOrigin.Begin);
//bitmap = new Bitmap(args.image);
bitmap = (Bitmap)Image.FromStream(args.image);
It gives me a "parameter is not valid" exception.
I have read that this is a buggy solution of Microsoft. Actually the
retreived stream is believed to be 21 bytes long because there is a trailing
zero \0 at the end of the leading "System.Drawing.Bitmap"\0. That is why the
processing of the image is stopped.
I have read a solution that avoids this:
byte[] arr = PdfReader.FlateDecode(PdfReader.GetStreamBytesRaw((PRStream)obj), true);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(width, height, pixelFormat);
System.Drawing.Imaging.BitmapData bmd = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.WriteOnly, pixelFormat);
Marshal.Copy(arr, 0, bmd.Scan0, arr.Length);
bmp.UnlockBits(bmd);
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png); // here it is the trick
arr = ms.GetBuffer(); // now I replace my incorrect image format bytes for the correct ones
System.Drawing.Image image = System.Drawing.Image.FromStream(ms);
image.Save(path, System.Drawing.Imaging.ImageFormat.Png);
}
How does the last arr = ... have any effect? It is not used anymore later?
So I don't understand.
update:
On a Microsoft website: https://social.msdn.microsoft.com/Forums/vstudio/en-US/f9e39595-04ca-42ae-a353-eb1a08602631/resolved-creating-image-file-parameter-is-not-valid?forum=netfxbcl
I rewrote the example so:
public static Bitmap DecodeJPEGFromMemoryStream(MemoryStream stream, int width, int height, PixelFormat pixelFormat)
{
string content = System.Text.Encoding.UTF8.GetString(stream.ToArray());
byte[] arr = Encoding.UTF8.GetBytes(content);
//byte[] arr = PdfReader.FlateDecode(PdfReader.GetStreamBytesRaw((PRStream)obj), true);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(width, height, pixelFormat);
System.Drawing.Imaging.BitmapData bmd = bmp.LockBits(new
System.Drawing.Rectangle(0, 0, width, height),
System.Drawing.Imaging.ImageLockMode.WriteOnly, pixelFormat);
Marshal.Copy(arr, 0, bmd.Scan0, arr.Length);
bmp.UnlockBits(bmd);
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); // here it is the trick
arr = ms.GetBuffer(); // now I replace my incorrect image format bytes for the correct ones
System.Drawing.Image image = System.Drawing.Image.FromStream(ms);
Bitmap ret = new Bitmap(image);
return ret;
}
}
I can see the image in the Picturebox, but it has only 2 lines, flickering.
If I obscure the camera I see that it darkens. So the data comes through now. But I cannot reconstruct my JPEG live data.
I was able to decode the JPEG data, only with saving it to disk.
So I've got the image without error. I tried to save the image only
temporarily and delete after.
public static Bitmap DecodeJPEGFromMemoryStream(MemoryStream stream, int width, int height, PixelFormat pixelFormat)
{
try
{
if(File.Exists("c:\\imagecache.jpg") == true)
File.Delete("c:\\imagecache.jpg");
//using (FileStream file = new FileStream("c:\\imagecache.jpg", FileMode.CreateNew, System.IO.FileAccess.ReadWrite))
using (FileStream file = File.Open("c:\\imagecache.jpg", FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite))
{
stream.WriteTo(file);
//JpegBitmapDecoder decoder = new JpegBitmapDecoder(file, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
file.Close();
file.Dispose();
}
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
//BitmapSource bitmapSource = decoder.Frames[0];
Bitmap jpeg;
{
{
FileInfo info = new FileInfo("c:\\imagecache.jpg");
if (info.Length == 0)
{
File.Delete("c:\\imagecache.jpg");
return null;
}
}
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
jpeg = (Bitmap)Image.FromFile("c:\\imagecache.jpg"); //the process cannot access the file...
}
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
if (File.Exists("c:\\imagecache.jpg") == true)
File.Delete("c:\\imagecache.jpg");
return jpeg;
}
catch(Exception ex)
{
return null;
}
}
I get the exception: The process cannot access the file "imagecache.jpg",
because it is being used by another process. Exception comes at the FromFile.
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
Bitmap retImage = (Bitmap)jpeg.Clone();
jpeg.Dispose();
if (File.Exists("c:\\imagecache.jpg") == true)
{
System.IO.FileInfo fi = new System.IO.FileInfo("c:\\imagecache.jpg");
fi.Delete();
}
return retImage;
}
catch (Exception ex)
{
return null;
}
finally
{
stream.Dispose();
}
It is fixed so. Image.FromFile: the File remains locked until the Image is disposed.
So I cloned my Image and disposed the old one.
Before I've seen my Image, now nothing is visible.
I have a datagridview with an image column on my form and I set its value to an image in some folder ..
Bitmap PicImage;
PicImage = new Bitmap(ImagePath);
Grid.Rows[i].Cells["ImageColumn"].Value = PicImage;
when I want to delete the row ,the image should be deleted too, but I get "the process cannot access the file..." message :
File.delete(ImagePath);
How can I solve it?
Use a file stream to unlock the file , so instead of:
PicImage = new Bitmap(ImagePath);
use:
using (var stream= new System.IO.FileStream(ImagePath, System.IO.FileMode.Open))
{
var bmp= new Bitmap(stream);
PicImage = (Bitmap) bmp.Clone();
}
Try first to unload the image from the Bitmap and then delete it.
The cleanest method is to load it without any links to files or streams. If it's just for showing on a UI, the simplest way to do this without deep-cloning using LockBits is simply to paint it on a new 32BPP ARGB image:
Bitmap image;
using (Bitmap tmpImage = new Bitmap(filepath))
{
image = new Bitmap(tmpImage.Width, tmpImage.Height, PixelFormat.Format32bppArgb);
using (Graphics gr = Graphics.FromImage(image))
gr.DrawImage(tmpImage, new Rectangle(0, 0, image.Width, image.Height));
}
// Bitmap "image" is now ready to use.
The following code unlinked the datagridview from the image file.
Bitmap timage;
using (Bitmap tmpImage = new Bitmap(tname))
{
timage = new Bitmap(tmpImage.Width, tmpImage.Height, PixelFormat.Format32bppArgb);
using (Graphics gr = Graphics.FromImage(timage))
gr.DrawImage(tmpImage, new Rectangle(0, 0, timage.Width, timage.Height));
}
fileGridView.Rows[dgIndex].Cells["thumbNail"].Value = timage;
Iam using this code to generate bitmap from string. Now i need to convert this bitmap into ICO or icon and below method is not working it causes error when i use System.Drawing.Imaging.ImageFormat.Icon error is Null Refrence Exception. and if i use any other format that error resolved and new error arise in m_notifyIcon that cannot use this as image. what to do...
System.Drawing.Bitmap bmp = TextToBitmap("This");//return bitmap
MemoryStream ms = new MemoryStream();
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Icon);
BitmapImage imgg = new BitmapImage();
imgg.BeginInit();
imgg.StreamSource = new MemoryStream(ms.ToArray());
imgg.EndInit();
//img.Source = imgg;
Icon io = new System.Drawing.Icon(ms);
m_notifyIcon.Icon = io;
Got answer to my question.
System.Drawing.Bitmap bmp = TextToBitmap("String");
System.IntPtr ich = bmp.GetHicon();
System.Drawing.Icon io = System.Drawing.Icon.FromHandle(ich);
m_notifyIcon.Icon = io;
get Bitmap iamge use IntPtr and use the above code...
I have this code (C#):
unsafe private Bitmap Test()
{
Bitmap test = null;
byte[] data = memRenderAll.CurrentData;
fixed (byte* m_pBuffer = _data)
{
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
Bitmap a = new Bitmap(720, 576, 2160, System.Drawing.Imaging.PixelFormat.Format24bppRgb, new IntPtr(m_pBuffer));
a.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
test =(Bitmap) Image.FromStream(ms);
a.Dispose();
}
}
return _test;
}
By saving the stream as this:
a.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
I get a 10:1 reduction is size.
Is there a way of avoiding this:
a.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
and specifying it somewhere/somehow in:
Bitmap a = new Bitmap(720, 576, 2160, System.Drawing.Imaging.PixelFormat.Format24bppRgb, new IntPtr(m_pBuffer));
as I would like to create the Bitmap only once.
thanks,
Andrew
I think its impossible todo in one code line using standard System.Drawing.
You use that constructor: http://msdn.microsoft.com/en-us/library/zy1a2d14(v=vs.110).aspx
Your code:
new Bitmap(720, 576, 2160, System.Drawing.Imaging.PixelFormat.Format24bppRgb, new IntPtr(m_pBuffer));
What does it mean? Bitmap read memory pointed by m_pBuffer using fixed width, height, stride and pixel format. You can't read it as jpeg, because jpeg - image zipping fomat. Look on it: http://en.wikipedia.org/wiki/JPEG#JPEG_codec_example . Jpeg codec need all your image for ziping, it can't zip parts and join them after.