| Aspect Ratio | Lab Report | ||
|
Chinese Translation by Hector Xiang |
|||
Purpose
The purpose of this project is to demonstrate
how to display a rectangular TBitmap in a rectangular TImage of any size, while preserving
the original aspect ratio.
Background
The ratio of a picture's width-to-height is known
as its aspect ratio. An image with an aspect ratio greater than 1, is
called a "landscape" image. An image with an aspect ratio less
than 1, is called a "portrait" image. When the aspect ratio is
exactly 1, the image is square.
This image has an aspect ratio of 4 to 3:
|
width = 200 |
|
| height = 150 |
|
Aspect Ratio = width / height = 200/150 = 4/3
(a "landscape" picture)
A larger or smaller "similar" version of a picture can be obtained by stretching both dimensions by the same factor, which keeps the aspect ratio the same. For example, if you divide each of the dimensions of the above picture by 2, the resulting image has the same aspect ratio:
|
width = 100 |
|
| height = 75 |
|
Aspect Ratio = width / height = 100/75 = 4/3
(a "landscape" picture)
A distorted picture results from stretching one dimension more or less than the other dimension. If the 200-by-150 image is stretched only in the vertical dimension, the picture is obviously distorted:
|
width = 200 |
|
| height = 50 |
|
Aspect Ratio = width / height = 200/50 = 4
Likewise, if the 200-by-150 image is only stretched in the horizontal dimension, the picture is obviously distorted also:
|
width = 50 |
|
| height = 150 |
|
Aspect Ratio = width / height = 50/150 = 1/3
(a "portrait" picture)
Many common computer display modes, and digital camera image sizes, involve a 4-to3 aspect ratio with square pixels:
Common 4-to-3 Aspect Ratio Sizes
| width | height | comments |
| 640 | 480 | standard VGA display |
| 800 | 600 | |
| 1024 | 768 | XGA Nikon CoolPix 990 image size |
| 1280 | 1024 | |
| 1400 | 1050 | Dell Inspiron display mode on 15" LCD |
| 1600 | 1200 | |
| 2048 | 1536 | "Full" Nikon CoolPix 990 image size |
Displaying a picture with a 4-to-3 aspect ratio full screen in a 4-to-3 display mode gives good results. Displaying a "landscape" picture in a "portrait" display area, or vice versa, often does not give acceptable results using Delphi's TImage component. Sometimes setting the Stretch property of a TImage can be used to fill an area, but this stretching results in distortion since each dimension may be be stretched by a different factor.
Finding a method to display any rectangular picture in a TImage of any size, AND preserving the aspect ratio, is very desirable.
Materials and Equipment
Software Requirements
Windows 95/98/NT/2000
Delphi 3/4/5 (to recompile)
AspectRatio.EXEHardware Requirements
VGA display with high color or true color.
Procedure
Press on the Load Picture button and select a picture (.BMP, .JPG, ...) to display from the OpenPictureDialog.
Observe how the picture is displayed in landscape, square and portrait orientations. The AspectRatio program shows each picture in two different sizes of landscape, square and portrait TImages.
If desired, change the "fill color" for the areas that are not covered by preserving the aspect ratio.
Observe all possible picture types and how they are displayed:
| TBitmap Type |
TImage Type |
Example |
||
| Landscape | Square | Portrait | ||
| Landscape | Best | "Sunflower" image (above) | ||
| Square | Best | "Smiley" image (below) | ||
| Portrait | Best | "Ski Lift" image (below) | ||
"Square" picture displayed in various rectangles while maintaining proper aspect ratio
"Portrait" picture displayed in various rectangles while maintaining proper aspect ratio
Discussion
See the source code for complete details of how the AspectRatio program works. The highlights are shown here.
Pressing the Load Picture button invokes the ButtonLoadPictureClick method shown in Listing 1.
|
Listing 1. Processing "Load Picture" Button Click |
procedure TFormAspectRatio.ButtonLoadPictureClick(Sender: TObject);
begin
IF OpenPictureDialog.Execute
THEN BEGIN
Bitmap.Free; // get rid of old bitmap
Bitmap := LoadGraphicsFile(OpenPictureDialog.Filename);
LabelFilename.Caption := OpenPictureDialog.Filename + ' (' +
IntToStr(Bitmap.Width) + ' by ' +
IntToStr(Bitmap.Height) + ' pixels)';
UpdateAllImages
END
end;
|
The LoadGraphicsFile routine uses a TPicture object to load a graphic of any registered file type. Before the picture can be manipulated in any way, it must be converted to a TBitmap, as shown in Listing 2.
|
Listing 2. Read TPicture and converting to TBitmap |
// Based on suggestions from Anders Melander. // See Magnifier Lab Report. FUNCTION LoadGraphicsFile(CONST Filename: STRING): TBitmap; VAR Picture: TPicture; BEGIN RESULT := NIL; IF FileExists(Filename) THEN BEGIN RESULT := TBitmap.Create;
TRY
Picture := TPicture.Create;
TRY
Picture.LoadFromFile(Filename);
// Try converting picture to bitmap
TRY
Result.Assign(Picture.Graphic);
EXCEPT
// Picture didn't support conversion to TBitmap.
// Draw picture on bitmap instead.
RESULT.Width := Picture.Graphic.Width;
RESULT.Height := Picture.Graphic.Height;
RESULT.PixelFormat := pf24bit;
RESULT.Canvas.Draw(0, 0, Picture.Graphic);
END
FINALLY
Picture.Free
END
EXCEPT
RESULT.Free;
RAISE
END
END
END {LoadGraphicFile};
|
LoadGraphicsFile (from Listing 2) is used to create the Bitmap object (in Listing 1). UpdateAllImages shown in Listing 1 displays this Bitmap object in each of the various TImages, which is shown in Listing 3.
Listing 3. Display Bitmap in various TImages
PROCEDURE TFormAspectRatio.UpdateAllImages; BEGIN DisplayBitmap(Bitmap, ImageLandscape); DisplayBitmap(Bitmap, ImageSquare); DisplayBitmap(Bitmap, ImagePortrait);DisplayBitmap(Bitmap, ImageLandscapeNarrow); DisplayBitmap(Bitmap, ImageSquareSmall); DisplayBitmap(Bitmap, ImagePortraitNarrow); END {UpdateAllImages};The DisplayBitmap routine creates a new bitmap that is the same size as the target TImage. Then DisplayBitmap copies the original bitmap to the new bitmap in such as way as to maintain the original aspect ratio and keep the new version as large as possible, as shown in Listing 4.
Listing 4. Create New Bitmap to Fill TImage
// Display Bitmap in Image. Keep the TBitmap as large as possible // in the TImage while maintaining the correct aspect ratio. PROCEDURE TFormAspectRatio.DisplayBitmap(CONST Bitmap: TBitmap; CONST Image : TImage); VAR Half : INTEGER; Height : INTEGER; NewBitmap : TBitmap; TargetArea: TRect; Width : INTEGER; BEGIN NewBitmap := TBitmap.Create; TRY NewBitmap.Width := Image.Width; NewBitmap.Height := Image.Height; NewBitmap.PixelFormat := pf24bit;NewBitmap.Canvas.Brush := ShapeFill.Brush; NewBitmap.Canvas.FillRect(NewBitmap.Canvas.ClipRect);// "equality" (=) case can go either way in this comparisonIF Bitmap.Width / Bitmap.Height < Image.Width / Image.Height THEN BEGIN// Stretch Height to match. TargetArea.Top := 0; TargetArea.Bottom := NewBitmap.Height;// Adjust and center Width. Width := MulDiv(NewBitmap.Height, Bitmap.Width, Bitmap.Height); Half := (NewBitmap.Width - Width) DIV 2;TargetArea.Left := Half; TargetArea.Right := TargetArea.Left + Width; END ELSE BEGIN // Stretch Width to match. TargetArea.Left := 0; TargetArea.Right := NewBitmap.Width;// Adjust and center Height. Height := MulDiv(NewBitmap.Width, Bitmap.Height, Bitmap.Width); Half := (NewBitmap.Height - Height) DIV 2;TargetArea.Top := Half; TargetArea.Bottom := TargetArea.Top + Height END;NewBitmap.Canvas.StretchDraw(TargetArea, Bitmap); Image.Picture.Graphic := NewBitmap FINALLY NewBitmap.Free END END {DisplayBitmap};The Canvas.StretchDraw method is used to stretch the bitmap, as shown in Listing 4 above. Other algorithms to stretch images can be found under Resampling on the Delphi Image Processing Algorithms page. In particular, use StretchDIBits to print a bitmap instead of StretchDraw shown above.
When the aspect ratio is fixed when the bitmap is stretched, part of the new bitmap may not be filled with the original bitmap. The FillRect call above fills the entire new bitmap with the color of defined by the brush of the TShape. This color remains in the areas not filled with the original bitmap.
Since a TShape does not have an OnClick method, the TShape's OnMouseDown is used to assign the color from a ColorDialog. Just click on the TShape to change this fill color.
Conclusion
Displaying a TBitmap in a TImage of the same
aspect ratio gives the best and easiest results, but with slight adjustments, a
bitmap can be displayed in any TImage while preserving the aspect ratio.
Keywords
aspect ratio, landscape orientation, portrait orientation, StretchDraw,
TPicture, TBitmap, TImage, TShape
Download
Delphi 3/4/5 Source and EXE (188 KB): AspectRatio.ZIP
| Updated | 14 Jun 2009 |
| Since | 23 Mar 2001 |