GDI+ FAQ: Writing a modified image to the original filename
A question that pops up all the time
is:
"I want to open an image file and then save that
same file in a different format or back to the same filename after making
modifications and I keep getting A generic error occurred in GDI"
This problem arises because GDI+ tries to be IO
efficient by reading in only the headers of an image, so that you can see
its width, height, colour depth etc. It leaves the file open and locked so
that it can go get the actual bits when you want to look at
them.
The file remains locked even after you've read all
the bits in, for the lifetime of the Image object in fact, and therefore
you cannot write back to that file with a modified version of the
image.
To have access to the file for writing, you must
dispose of the original image, which makes it difficult to save it
afterwards.
The trick is to do the following:
- Open the image file
- create a temporary in-memory image the same size
as the original
- obtain a Graphics object for the temporary
image
- Draw the original image onto the temporary
one
- dispose of the original
- do any drawing you'd like on the image using the
Graphics you have
- Dispose of the Graphics
- Write the temporary file to any filename and any
format you please including the same name and format if you
wish.
The following application combines a file-format
converter with a picture time-stamper that shows off this
technique.

- Click "Choose files" to select one or more image
files.
- Choose the output format in the dropdown
- Decide whether you want a timestamp or not and
click "Convert"
- If you choose jpeg files as input, a timestamp
and jpeg files as output you will open the files, stamp them and write
them back to the same filename again (Don't do this with files that you
want to keep pristine!)
The following listing shows how. All the hard work
is done in the button2_click handler.
using System;
using System.IO;
using System.Drawing;
using
System.Drawing.Imaging;
using
System.Collections;
using
System.ComponentModel;
using
System.Windows.Forms;
using System.Data;
namespace FormatConvertor
{
/// <summary>
/// Summary
description for Form1.
/// </summary>
public class
Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button
button1;
private System.Windows.Forms.ComboBox
comboBox1;
private System.Windows.Forms.Button
button2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.CheckBox
checkBox1;
/// <summary>
/// Required
designer variable.
/// </summary>
private System.ComponentModel.Container
components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after
InitializeComponent call
//
}
/// <summary>
/// Clean up any
resources being used.
/// </summary>
protected override void
Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer
generated code
/// <summary>
/// Required method
for Designer support - do not modify
/// the contents of
this method with the code editor.
/// </summary>
private void
InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.comboBox1 = new System.Windows.Forms.ComboBox();
this.button2 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.checkBox1 = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(8, 24);
this.button1.Name = "button1";
this.button1.TabIndex = 0;
this.button1.Text = "Choose files";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// comboBox1
//
this.comboBox1.Items.AddRange(new object[]
{
"GIF",
"JPG",
"JPEG",
"BMP",
"PNG"});
this.comboBox1.Location = new System.Drawing.Point(96, 24);
this.comboBox1.Name = "comboBox1";
this.comboBox1.Size = new System.Drawing.Size(121, 21);
this.comboBox1.TabIndex = 1;
//
// button2
//
this.button2.Location = new System.Drawing.Point(232, 24);
this.button2.Name = "button2";
this.button2.TabIndex = 0;
this.button2.Text = "Convert";
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// label1
//
this.label1.Location = new System.Drawing.Point(96, 8);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(100, 16);
this.label1.TabIndex = 2;
this.label1.Text = "Output format";
//
// checkBox1
//
this.checkBox1.Location = new System.Drawing.Point(8, 64);
this.checkBox1.Name = "checkBox1";
this.checkBox1.Size = new System.Drawing.Size(208, 24);
this.checkBox1.TabIndex = 3;
this.checkBox1.Text = "Time stamp the
image.";
//
//
Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(312, 93);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.checkBox1,
this.label1,
this.comboBox1,
this.button1,
this.button2});
this.FormBorderStyle =
System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Name = "Form1";
this.Text = "Image Format Converter";
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry
point for the application.
/// </summary>
[STAThread]
static void
Main()
{
Application.Run(new Form1());
}
string[] names;
bool chosen=false;
private void
button1_Click(object sender,
System.EventArgs e)
{
OpenFileDialog
dlg = new OpenFileDialog();
dlg.Filter="Image
files
(*.jpg,*.jpeg,*.bmp,*.gif,*.png)|*.BMP;*.PNG;*.JPG;*.JPEG;*.PNG";
dlg.Multiselect=true;
if(dlg.ShowDialog()==DialogResult.OK)
{
chosen=true;
names=dlg.FileNames;
}
}
private void
button2_Click(object sender,
System.EventArgs e)
{
if(chosen && this.comboBox1.Text!="")
{
chosen=false;
foreach(string
s in names)
{
//open the file
Image
i = Image.FromFile(s);
//create temporary
Image
t=new
Bitmap(i.Width,i.Height);
//get graphics
Graphics
g=Graphics.FromImage(t);
//copy original
g.DrawImage(i,0,0);
//close original
i.Dispose();
if(this.checkBox1.Checked==true)
{
//draw on temporary
Font
f = new Font("Verdana",30);
g.DrawString(DateTime.Now.ToShortDateString()+":"+DateTime.Now.ToShortTimeString(),
f,
Brushes.Red,
10,10,
StringFormat.GenericTypographic);
f.Dispose();
}
g.Dispose();
ImageFormat
fmt = ImageFormat.Bmp;
switch(this.comboBox1.Text)
{
case "JPG":
case "JPEG":
fmt=ImageFormat.Jpeg;
break;
case "GIF":
fmt=ImageFormat.Gif;
break;
case "PNG":
fmt=ImageFormat.Png;
break;
}
//save the file. Even if its the same filename
t.Save(Path.GetFileNameWithoutExtension(s)+"."+this.comboBox1.Text,fmt);
}
}
}
}
}
Back to the GDI+
FAQ
|