
Calculating Best-Fit Drawing Using A
Transform
Often, an image needs to
be displayed in a rectangle that is not the same size or aspect ratio as the
original image. One solution is to make a temporary copy of an image, perhaps a
thumbnail, and use this to display the image. Another method often used is to
simply stretch the image into the rectangle and hope for the best. This is the
PictureBox controls best-fit method. However, in cases where the aspect ratio
of the image is important stretching is a poor choice. My favourite method
employs the power of GDI+ transforms to draw the image directly without making
a copy. A transform can be calculated that fits one dimension of the
image into the shortest dimension of the destination rectangle, An offset can
also be calculated that moves the image into the horizontal or vertical centre of the destination rectangle. The resulting transform is then used to draw the
image directly into the destination without having to copy the image. The images in Figure 1 show how the image is displayed on a form after transformation.
Figure 1 The code that performs
this is shown in listing 1. The work is all handled in the Form1_Paint routine. using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; namespace bestfit { /// <summary> /// Summary description for Form1. /// </summary> public class Form1 : System.Windows.Forms.Form { public
Image _image=null; /// <summary> /// Required designer variable. /// </summary> private
System.ComponentModel.Container components = null; public
Form1() { // // Required
for Windows Form Designer support // InitializeComponent(); SetStyle( ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer, true); } /// <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() { // // Form1 // this.AutoScaleBaseSize
= new System.Drawing.Size(5, 13); this.ClientSize
= new System.Drawing.Size(464, 261); this.Name
= "Form1"; this.Text
= "Form1"; this.Load
+= new System.EventHandler(this.Form1_Load); this.Paint
+= new System.Windows.Forms.PaintEventHandler(this.Form1_Paint); } #endregion /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.Run(new
Form1()); } private void Form1_Load(object
sender, System.EventArgs e) { OpenFileDialog dlg= new OpenFileDialog(); dlg.Filter="Image files (*.bmp,
*.jpg, *.gif)|*.BMP;*.JPG;*.GIF"; if(dlg.ShowDialog()==DialogResult.OK) { this._image=Image.FromFile(dlg.FileName); Invalidate(); } } private void Form1_Paint(object
sender, System.Windows.Forms.PaintEventArgs e) { if(_image==null) return; double
largestRatio=Math.Max((double)_image.Width/this.ClientSize.Width,(double)_image.Height/this.ClientSize.Height); float posX=(float)(this.ClientSize.Width*largestRatio/2-_image.Width/2); float posY=(float)(this.ClientSize.Height*largestRatio/2-_image.Height/2); Matrix mx=new
Matrix(1.0f/(float)largestRatio,0,0,1.0f/(float)largestRatio,0,0); mx.Translate(posX,posY); e.Graphics.Transform=mx; e.Graphics.DrawImageUnscaled(_image,0,0); } } } Copyright
Robert W. Powell 2003. All rights reserved. |