Tuesday, November 15, 2005

C# - Resources – Manifest Resources

Image can be loaded into the application from the file system, for example:
 // Load image from a file path
 Bitmap image = new Bitmap(@"c:\ufo.gif");
But the problem with this method is the application cannot guarantee the file it's looking for will be there. The reasons could be the user removed or moved the file to other location. If the image file the application expecting does not exists, it will cause the application not functioning properly.

The only safe way to make sure that the image, or any file, stays with code is to embed it and load it as a resource, a named piece of data embedded in the assembly itself.

Following command line shows how to embed a file into the assembly:
 csc [source file name.cs] /resource:[resource file name]

 csc ufo.cs /resource:UFOApplication.ufo.gif
The image file will embedded in the assembly’s set of manifest resources. The manifest of an assembly is composed of a set of metadata that’s part of the assembly. Part of that metadata is the name and data associated with each embedded resource.

Following codes shows how to retrieve resources from the assembly manifest:
using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;

namespace UFOApplication {
 public class Form1: Form {

  private Assembly assembly;
  private Bitmap image;

  // Constructor
  public Form1() {

   // Get current object type's assembly.
   this.assembly = this.GetType().Assembly;

   // Load the image binary stream from the assembly manifest.
   // Stream stream = this.assembly.GetManifestResourceStream("UFOApplication.ufo.gif");
   Stream stream = this.assembly.GetManifestResourceStream(this.GetType(), "ufo.gif");

   // Construct the bitmap from stream.
   this.image = new Bitmap(stream);  

   this.Paint += new PaintEventHandler(Form1_Paint);
  }

  private void Form1_Paint(object sender, PaintEventArgs e) {
   // Draw the bitmap on the form.
   e.Graphics.DrawImage(this.image, 0, 0); 
  }

  // The main entry point for the application.
  public static void Main() {
   Application.Run(new Form1());
  }
 }
}
Although manifest resources are useful, but there are some limitations. For example, manifest resources are embedded with no type information. So there will be no difference to the Bitmap class for ufo.gif, ufo.jpg, or ufo.xyz, which looking for data itself for the type like JPEG, PNG, GIF and etc.

The proper type of object will be needed to load the relevant type of each resource.

Monday, November 14, 2005

C# - Graphics – Double Buffering

The codes below is to load a gif image from the local path and draw it on the form, and the image will “animated” by moving 1 step down and right at certain interval. The image must exist in the same path as the application executable. This program has a timer component that executes the codes to increase the X and Y values (for the image), and refresh the screen by calling the form’s Invalidate method. Invalidate will trigger the form’s Paint Event to redraw the form’s client area. The Paint Event will draw the image on the form’s client area with the newly updated X and Y values (the horizontal point, X-axis, and the vertical point, Y-axis).
using System;
using System.Windows.Forms;
using System.Drawing;

public class Form1: Form {
 private Timer timer1;
 private Image imgUFO;

 private int x = 0;
 private int y = 0;

 // Constructor
 public Form1() {
  this.SuspendLayout();

  // Load UFO bitmap from the same path as the running application.
  this.imgUFO = new Bitmap(@"ufo.gif");

  // Setting up the Timer control.
  this.timer1 = new Timer();
  this.timer1.Interval = 50;
  this.timer1.Enabled = true;
  this.timer1.Tick += new EventHandler(timer1_Tick);

  // Setting up Form control.
  this.Text = "UFO Program";
  this.BackColor = Color.White;
  this.Paint += new PaintEventHandler(Form1_Paint);

  this.ResumeLayout(false);
 }

 private void timer1_Tick(object sender, EventArgs e) {
  // Update image position
  newPositions();

  // Refresh screen
  Invalidate();
 }

 private void Form1_Paint(object sender, PaintEventArgs e) {
  // Draw bitmap on the form.
  Graphics g = e.Graphics;
  g.DrawImage(this.imgUFO, this.x, this.y); 
 }

 private void newPositions() {
  // Move Right & Down for 1 step.
  this.x += 1;
  this.y += 1;
 }

 // The main entry point for the application.
 public static void Main() {
  Application.Run(new Form1());
 }
}
The program seems to work perfectly. But when notice carefully, the image draw on screen will look a bit flickering. The image flashes on and off most unpleasantly. This is due to the way that the screen is drawn. When the Paint Event redraws the form, the screen is drawn with white, and then the image is drawn separately. This process is very visible and looks terrible.

This problem can be solved by a technique known as double buffering. The idea is that the program doesn't draw on the frame the user can see. Instead, it draws on a buffer in memory. Then, when the picture is complete, it is copied into the visible screen. The copy process is very quick, and the flicker is removed.

The following codes create another bitmap area, draw onto that, and then copy the new bitmap into position:
using System;
using System.Windows.Forms;
using System.Drawing;

public class Form1: Form {
 private Timer timer1;
 private Image imgUFO;

 private int x = 0;
 private int y = 0;

 // Constructor
 public Form1() {
  this.SuspendLayout();

  // Load UFO bitmap from the same path as the running application.
  this.imgUFO = new Bitmap(@"ufo.gif");

  // Setting up the Timer control.
  this.timer1 = new Timer();
  this.timer1.Interval = 50;
  this.timer1.Enabled = true;
  this.timer1.Tick += new EventHandler(timer1_Tick);

  // Setting up Form control.
  this.Text = "UFO Program";
  this.BackColor = Color.White;
  this.Paint += new PaintEventHandler(Form1_Paint);

  this.ResumeLayout(false);
 }

 private void timer1_Tick(object sender, EventArgs e) {
  // Update image position
  newPositions();

  // Refresh screen
  Invalidate();
 }

 // This is a very important method overriding for double buffering.
 // By overriding this method, it will disable the background painting.
 protected override void OnPaintBackground(PaintEventArgs e) {
 }

 private void Form1_Paint(object sender, PaintEventArgs e) {
  // Temporary bitmap to hold the backbuffer image
  Bitmap backBuffer = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);

  using (Graphics g = Graphics.FromImage(backBuffer)) {
   g.Clear(Color.White);
   g.DrawImage(this.imgUFO, this.x, this.y);
  }

  e.Graphics.DrawImage(backBuffer, 0, 0);
 }

 private void newPositions() {
  // Move Right & Down for 1 step.
  this.x += 1;
  this.y += 1;
 }

 // The main entry point for the application.
 public static void Main() {
  Application.Run(new Form1());
 }
}