I have this code
private void saveImage()
{
Bitmap bmp1 = new Bitmap(pictureBox.Image);
bmp1.Save("c:\\t.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
// Dispose of the image files.
bmp1.Dispose();
}
i already have an image t.jpg at my drive "c:\".
i wanted to replace it with a new image every time my program runs. but a GDI+ error shows up
how could i fix it?
You must remove your image if that is already exists.
private void saveImage()
{
Bitmap bmp1 = new Bitmap(pictureBox.Image);
if(System.IO.File.Exists("c:\\t.jpg"))
System.IO.File.Delete("c:\\t.jpg");
bmp1.Save("c:\\t.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
// Dispose of the image files.
bmp1.Dispose();
}
I presume you earlier loaded the c:\t.jpg image using the Image.Load method. If so, the Image object is holding an open file handle on the image file, which means that the file can't be overwritten.
Instead of using Image.Load to get the original image, load it from a FileStream that you create and dispose of.
So, instead of
Image image = Image.Load(#"c:\\t.jpg");
do this:
using(FileStream fs = new FileStream(#"c:\\t.jpg", FileMode.Open))
{
pictureBox.Image = Image.FromStream(fs);
fs.Close();
}
The file handle has been released so overwriting the file with Bitmap.Save can succeed. The code you gave in your question should therefore work. There is no need to delete the original file or dispose of the image before saving.
Additional:
If you close the FileStream as above,then calls to Image.Save will throw an exception. See here: A Generic error occurred in GDI+ in Bitmap.Save method
Related
I'm trying to save a System.Drawing.Image to a File.
This is my code:
using(WebClient webClient = new WebClient())
webClient.DownloadFile(new Uri(defaultPic), defaultPicPath);
var tempImg = System.Drawing.Image.FromFile(defaultPicPath);
using(System.Drawing.Image RoundedImage = RoundCorners(tempImg, 90, System.Drawing.Color.Transparent)) {
RoundedImage.Save(defaultPicPath);
tempImg.Dispose();
}
In the RoundedImage.Save(...); line, I'm getting the Error
General Error in GDI+.
I am pretty sure the File is not locked by any WPF Control or Image.
Any Help is appreciated!
Thanks!
From what I can see from your code, you are not releasing the file.
You should,
First read the image and create a local deep copy of it(tempImage). (Note: Shallow copy using Clone method will not work.)
Release the image file.
Modify your local copy.
Overwrite the original file.
Dispose local copy(tempImage).
Try following, it should work;
Image tempImage;
using(var img = System.Drawing.Image.FromFile(defaultPicPath))
tempImage=new Bitmap(img);
using(System.Drawing.Image RoundedImage = RoundCorners(tempImg, 90, System.Drawing.Color.Transparent))
RoundedImage.Save(defaultPicPath);
tempImg.Dispose();
I have 2 pieces of sample codes, I try to do the same thing that update property items back to image and save it under a different file.
Code 1
Image image;
using (FileStream stream = new FileStream(this.fileName, FileMode.Open)) {
image = Image.FromStream(stream);
foreach (var property in this.propItems) {
image.SetPropertyItem(property);
}
}
image.Save(#"D:\Temp\1.jpg");
image.Dispose();
Code 2
using (Image image = new Bitmap(this.fileName)) {
foreach (var property in this.propItems) {
image.SetPropertyItem(property);
}
image.Save(#"D:\Temp\1.jpg");
}
The only difference is that how I opened the file. If I run the first piece of code I got exception message
System.Runtime.InteropServices.ExternalException was unhandled
HResult=-2147467259 Message=A generic error occurred in GDI+.
Source=System.Drawing ErrorCode=-2147467259
My 2nd piece of code just runs fine, I can get proper output. What is the difference here?
This is by design, the MSDN article for Image.FromStream() sternly warns about this. Once you close the stream, the image is no longer usable. And trying to save it like you do is very likely, but not guaranteed, to throw an exception when it tries to retrieve pixel data from a closed stream.
A key property of the Image class is that it is lazy, not unlike many .NET classes, it won't access the stream data until necessary. And it isn't necessary until the pixel data is actually used, that happens in the Save() call in your snippet. Kaboom when it can no longer read it.
You can fix your first snippet by moving the Save() call inside the using statement:
using (var stream = new FileStream(this.fileName, FileMode.Open))
using (var image = Image.FromStream(stream) {
foreach (var property in this.propItems) {
image.SetPropertyItem(property);
}
image.Save(#"D:\Temp\1.jpg");
}
No point in using a FileStream anymore. Do note that the file that you save the image to cannot be the same file that you read the image from. Some hint that you tried to work around that problem. Using a MemoryStream is a common technique to avoid the lock on the file.
Also note another bug in your code, you save the file with the .jpg extension but it is actually a PNG. You cannot omit the ImageFormat argument if you want a JPEG.
I have searched throughout entire Stack Overflow, but I couldn't find an answer to the following:
When I'm using my OpenFileDialog, the files I open get blocked for use out of my program until I close my program. So if I open an image, I am not allowed to replace that image in my Windows Explorer anymore.
I think this is a problem with disposing my OpenFileDialog, but I'm not sure how to solve it...
My code:
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Title = "Open Image";
ofd.Filter = "PNG Image(*.png|*.png" +
"|GIF Image(*.gif|*.gif" +
"|Bitmap Image(*.bmp|*.bmp" +
"|JPEG Compressed Image (*.jpg|*.jpg";
if (ofd.ShowDialog() == DialogResult.OK)
{
pictureBox1.Image = new Bitmap(ofd.FileName);
}
}
I thought that the using block would solve this problem, but nope... It still gets used by the program. I want to load the image in the picturebox and then I want the image to be available again (so I can rename it, replace it, etc...).
This is not related to the OpenFileDialog. It couldn't possibly be, because the dialog doesn't actually open the file. It just allows the user to select a file to open, and then returns that path to you so that you can write code that opens the file. Besides, you're correctly disposing of the OpenFileDialog through your use of the using statement.
The problem here comes from your actually opening the file—ofd.FileName—as a Bitmap. When you use the Bitmap constructor overload that accepts a path string, the file on disk that contains the image remains locked until the Bitmap object is disposed. So says the documentation:
The file remains locked until the Bitmap is disposed.
Because you're assigning the bitmap to pictureBox1.Image, the Bitmap object will not be disposed until pictureBox1 is disposed. And therefore your image file on disk will remain locked.
If you want to unlock the file, you will either need to make a copy of the bitmap and dispose the original, or clear out the PictureBox and dispose its previous image when you are finished with it.
As I understand your question, it sounds like you want to be able to make changes to the image file on disk while continuing to display the image in the picture box. If that's the case, you will need to make a copy. Do that using the constructor overload that takes an Image, like this:
if (ofd.ShowDialog() == DialogResult.OK)
{
// Load the image the user selected
using (Image img = Image.FromFile(ofd.FileName))
{
// Create a copy of it
Bitmap bmpCopy = new Bitmap(img);
// Clear out the bitmap currently in the picture box,
// if there is one.
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
// Assign the copy of the bitmap to the picture box.
pictureBox1.Image = bmpCopy;
}
}
As written by Chris, try something like:
pictureBox1.Image = Image.FromStream(new MemoryStream(File.ReadAllBytes(old.FileName)));
It reads all the file with File.ReadAllBytes, put it in a MemoryStream and pass the MemoryStream to the Image static initializer.
Equivalent to:
byte[] bytes = File.ReadAllBytes(old.FileName);
MemoryStream ms = new MemoryStream(bytes);
pictureBox1.Image = Image.FromStream(ms);
You mustn't dispose the MemoryStream! If/when the Image will be disposed, the finalizer of MemoryStream will kick in (if you don't have other references to ms) and the MemoryStream will be disposed (note that this isn't something that will happen immediately... It's something that will happen when the GC will run)
The technique I've found to be best is to read the file into a byte array with File.ReadAllBytes() (that opens and closes the file), and then use ImageConverter to convert the byte array into an Image. See here for example: https://stackoverflow.com/a/16576471/253938
Edit:
Quote from that previous post of mine: "Some of the other techniques I've tried have been non-optimal because they changed the bit depth of the pixels (24-bit vs. 32-bit) or ignored the image's resolution (dpi)."
Overview of my application: On the client side, a series of snapshots are taken with a webcam. On submit, I want the images to be converted to a byte array, and have that byte array sent to a service I have written.
My problem: I'm trying to save a single image to a MemoryStream, but it continues to break, spitting out the message, "A generic error occured in GDI+." When I dig deeper, I see that the exception is thrown when the MemoryStream's buffer position is at 54. Unfortunately, it's a 1.2 mb photo. Here's the block of code:
// Create array of MemoryStreams
var imageStreams = new MemoryStream[SelectedImages.Count];
for (int i = 0; i < this.SelectedImages.Count; i++)
{
System.Drawing.Image image = BitmapFromSource(this.SelectedImages[i]);
imageStreams[i] = new MemoryStream();
image.Save(imageStreams[i], ImageFormat.Bmp); /* Error is thrown here! */
}
// Combine MemoryStreams into a single byte array (Threw this
// in in case somebody has a better approach)
byte[] bytes = new byte[imageStreams.Sum(s => s.Length)];
for(int i = 0; i < imageStreams.Length; i++)
{
bytes.Concat(imageStreams[i].ToArray());
}
And here is my BitmapFromSource method
// Converts a BitmapSource object to a Bitmap object
private System.Drawing.Image BitmapFromSource(BitmapSource source)
{
System.Drawing.Image bitmap;
using (MemoryStream ms = new MemoryStream())
{
BitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(ms);
bitmap = new System.Drawing.Bitmap(ms);
}
return bitmap;
}
A lot of what I have read about the Generic GDI+ Error points to a permissions issue, but I don't see how that would apply here, considering I'm not saving to the file system. Also, I've seen that this error can arise due to the MemoryStream closing before the image is being saved, but I also don't see how this would apply considering I create the MemoryStream immediately before I save the image. Any insight would be greatly appreciated.
I think your problem actually lies in your BitmapFromSource method.
You're creating a stream, then creating a bitmap from that stream, then throwing the stream away, then trying to save the bitmap to another stream. However, the documentation for the Bitmap class says:
You must keep the stream open for the lifetime of the Bitmap.
By the time you come to save that bitmap, the bitmap is already corrupted because you've thrown the original stream away.
See: http://msdn.microsoft.com/en-us/library/z7ha67kw
To fix this (bearing in mind I've not written the code let alone tested it), create the MemoryStream inside the first for loop in your first block of code, and pass that memory stream to your BitmapFromSource method as a second parameter.
Please see SecurityException when calling Graphics.DrawImage which leads to Common Problems with rendering Bitmaps into ASP.NET OutputStream
public void EditAndSave(String fileName) {
Bitmap b = new Bitmap(fileName);
/**
* Edit bitmap b... draw stuff on it...
**/
b.Save(fileName); //<---------Error right here
b.Dispose();
}
My code is similar to the above code. When i try to save the file i just opened, it won't work. When i try saving it with a different path, it works fine. Could it be that the file is already open in my program so it cannot be written to? I'm very confused.
You are correct in that it is locked by your program and therefore you can't write to it. It's explained on the msdn-page for the Bitmap class (http://msdn.microsoft.com/en-us/library/3135s427.aspx).
One way around this (from the top of my head, might be easier ways though) would be to cache image in a memorystream first and load it from there thus being able to close the file lock.
static void Main(string[] args)
{
MemoryStream ms = new MemoryStream();
using(FileStream fs = new FileStream(#"I:\tmp.jpg", FileMode.Open))
{
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, buffer.Length);
}
Bitmap bitmap = new Bitmap(ms);
// do stuff
bitmap.Save(#"I:\tmp.jpg");
}
I've not been in exactly the same situation as you, but I have had problems with open image files being kept open.
One suggestion is that you load the image into memory using the BitmapCacheOption.OnLoad, see my answer to this post: Locked resources (image files) management.
Then, after editing, that image can be saved to the the same file using a FileStream, that is described for example here: Save BitmapImage to File.
I'm not sure it is the easiest or most elegant way, but I guess it should work for you.
Indeed there may be many reasons for seeing this exception.
Note that if you're using PixelFormat.Format16bppGrayScale, it's not supported in GDI+ and fails in the way shown. It seems the only workaround for using proper 16-bit gray scale is to use the newer System.Windows.Media namespace from WPF.