There are actually several issues with your code:
- You're not closing the file stream of the image you opened, meaning you will never be able to save the modified image to the same filename. In fact, no program will be able to overwrite the file until your program is closed.
- You don't check if the edit is possible; open an image that is less than 51 pixels in either dimension and the save operation will crash.
- You're only offering to save as jpeg... a format in which a single pixel change will inevitably be smudged by the compression.
- You're not actually saving as jpeg. The default save without specifying a type will make the image end up as png format. It'll just have the wrong file extension.
- As ilyas varol pointed out, you're saving the unmodified
File
instead of the edited image
.
The basic rules to remember when opening images are these:
To get around the problem of the unclosed stream, you can load the image like this instead:
private void Import_Click(object sender, EventArgs e)
{
OpenFileDialog f = new OpenFileDialog();
f.Filter = "Image files (*.jpg, *.png)|*.jpg;*.png";
if (f.ShowDialog() != DialogResult.OK)
return;
using (Bitmap img = new Bitmap(f.FileName))
{
// if a file was already opened, dispose the old one.
// Start by setting the shown image to null so it
// will never try to show the disposed object.
pictureBox1.Image = null;
if (File != null)
{
try { File.Dispose(); }
catch { /* ignore */ }
}
// Clone loaded image into new object
File = new Bitmap(img);
}
pictureBox1.Image = File;
}
The new Bitmap(image)
constructor is one of the few safe ways to make a clone of an image object that's completely disconnected from the original loaded source. It does have the side effect of converting the image to 32-bit colour, if it was in a different colour format before.
There's another issue here, though. In the export function, you are once again making a new Bitmap
that you do not dispose. Why do you make that clone? Is there any issue with using File
directly? If there isn't, you should just edit it directly, and the pixel change will be shown on the UI as well. If you do want to only apply the change to the saved picture, again, use a using
block for the new object, so the image resources in memory are cleaned up automatically afterwards.
Also, you should really only perform the operation once the user has actually confirmed the filename. Your code performs it even if they press "Cancel", because it's performed before you even show the dialog.
private void Export_Click(object sender, EventArgs e)
{
SaveFileDialog f = new SaveFileDialog();
f.Filter = "Png Image (.png)|*.png";
if (f.ShowDialog() != DialogResult.OK)
return;
// OPTION 1: Edit "File" directly:
// Don't edit without doing the necessary checks.
if (File.Width > 50 && File.Height > 50)
File.SetPixel(50, 50, Color.Black);
File.Save(f.FileName, ImageFormat.Png);
// OPTION 2: Edit clone without affecting "File":
using (Bitmap image = new Bitmap(File))
{
// Don't edit without doing the necessary checks.
if (image.Width > 50 && image.Height > 50)
image.SetPixel(50, 50, Color.Black);
image.Save(f.FileName, ImageFormat.Png);
}
}