Title: Separate glyphs in an image in C#, Part 1
I'm working on a program to translate to and from Futurama's alienese alphabet. Before I do that, I wrote this program to separate glyphs stored in an image that contains the alphabet. It's a fairly full-featured program, so I'll describe in in a couple of posts. In this post, I'll explain how the program's menus work. In the next post, I'll explain the program's other pieces such as how the glyph selection rectangle works.
Using the Program
To use the program, you use the File menu's Open command to open an image containing the glyphs. To separate glyphs, you then drag a red glyph selection rectangle around on the image to select a glyph. You can then save the selected area in an image file named so you can tell which glyph it contains.
The program has a couple of other extra features. For example, it lets you resize the glyph selection rectangle and it lets you use arrow keys to move the selection rectangle more precisely.
Controls
The design is straightforward. The form simply contains a PictureBox and a menu. The menu has four menu items:
- File
- Open - Opens a file containing the glyphs
- Save Glyph - Saves the currently selected glyph
- Exit - Closes the program
- Data
- Dimensions - Lets you specify the glyphs' dimensions
The following selections show how the program implements its menu commands.
File ⮞ Open
The program uses the following code to define a Bitmap to hold the glyph image.
// The image containing the glyphs.
private Bitmap GlyphsBm = null;
When you select the File menu's Open command, the following code executes.
// Open a glyph file.
private void mnuFileOpen_Click(object sender, EventArgs e)
{
Cursor = Cursors.WaitCursor;
Refresh();
if (ofdFile.ShowDialog() == DialogResult.OK)
{
try
{
GlyphsBm = LoadBitmapUnlocked(ofdFile.FileName);
picGlyphs.ClientSize = GlyphsBm.Size;
picGlyphs.Visible = true;
mnuFileSaveGlyph.Enabled = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Cursor = Cursors.Default;
}
This code displays the OpenFileDialog named ofdFile. If you select a file and press Open, the code uses the LoadBitmapUnlocked method to load the image file without locking it. (For information on that method, see Load images without locking their files in C#.)
The code saves the bitmap in the GlyphsBm variable and makes the PictureBox visible. That raises that control's Paint event hanlder, which I'll describe later. The code also enables the File ⮞ Save Glyph, which is described next.
File ⮞ Save Glyph
When you invoke this command, the following code executes.
// Save a glyph.
private void mnuFileSaveGlyph_Click(object sender, EventArgs e)
{
if (sfdGlyph.ShowDialog() == DialogResult.OK)
{
using (Bitmap bm = new Bitmap(GlyphRect.Width, GlyphRect.Height))
{
using (Graphics gr = Graphics.FromImage(bm))
{
Rectangle dest_rect = new Rectangle(
0, 0, GlyphRect.Width, GlyphRect.Height);
gr.DrawImage(GlyphsBm, dest_rect, GlyphRect,
GraphicsUnit.Pixel);
}
SaveImage(bm, sfdGlyph.FileName);
}
}
}
This code displays the SaveFileDialog named sfdGlyph. If you select a file and click Save, the program creates a Bitmap with the dimensions of the GlyphRect. (GlyphRect is a Rectangle that I'll describe later when I talk about mouse and keyboard events.) It then makes an associated Graphics object.
Note that the code uses a using block for the Bitmap and Graphics objects so they are automatically disposed when the program is done with them.
Next, the code makes a Rectangle covering the entire Bitmap. It then calls the Graphics object's DrawImage method to copy the part of the glyphs image onto the Bitmap. (The GlyphRect rectangle already holds the area that should be copied so we don't need to make a new rectangle to define that area.)
After copying the part off the image, the code calls the SaveImage method to save the image with an appropriate format for the filename's extension. For example, if the file's name ends with .png, then SaveImage saves the image with the PNG format. (For more information about that method, see Save images with an appropriate format depending on the file name's extension in C#.)
File ⮞ Exit
This one is easy.
// Close the application.
private void mnuFileExit_Click(object sender, EventArgs e)
{
Close();
}
Data ⮞ Dimensions
This command displays the dialog on the right where you can enter new dimensions for the glyph selection rectangle.
The dialog is a simple Windows Form that contains a Label, TextBox, and two buttons. I only set a few properties to make the dialog work.
The form's AcceptButton and CancelButton properties are set to the two buttons. Setting those properties automatically sets the Cancel button's DialogResult property to Cancel. When you click this button, it sets the form's DialogResult property to Cancel and that closes the dialog.
Setting the AcceptButton property does not set the OK button's DialogResult property. The thought is that you might want that button to do some error checking or other processing before closing the dialog. I don't want that in this example, so I set the button's DialogResult property to OK. Now when you click this button, it sets the form's DialogResult property so the dialog closes.
The last important property I set was the text box control's Modifiers property. By default, a form's controls are only visible to code within the form. I set this property to Public so code int he main program can see the control. (A more "pure" way to do this would be to give the form a property that gets and sets the control's value, but that's extra work for not much benefit in this example.)
The following code shows how the program uses the dialog.
// Let the user select the glyph dimensions.
private void mnuDataDimensions_Click(object sender, EventArgs e)
{
DimensionsDialog dlg = new DimensionsDialog();
dlg.txtDimensions.Text = GlyphRect.Width.ToString() +
" x " + GlyphRect.Height.ToString();
if (dlg.ShowDialog() == DialogResult.OK)
{
try
{
string[] fields = dlg.txtDimensions.Text.ToLower().Split('x');
int width = int.Parse(fields[0]);
int height = int.Parse(fields[1]);
GlyphRect = new Rectangle(GlyphRect.X, GlyphRect.Y,
width, height);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
picGlyphs.Refresh();
}
}
The code creates a new DimensionsDialog and sets its text box's text to show the current width and height of the glyph selection rectangle.
Next, the code displays the dialog. If you click OK, the code parses the text that you entered and updates the glyph selection rectangle's dimensions.
After it is done, the code refreshes the PictureBox to make it show the new selection rectangle.
Summary
That's the end of the program's menu code. In my next post, I'll explain how the program manages its glyph selection rectangle.
Download the example to experiment with it and to see additional details.
|