Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
How to do frame animation with the TImageList class Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi 2.x
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			Author: Tomas Rutkauskas

How to do frame animation with the TImageList class


As users become more savvy, they begin to expect more sophisticated features from 
every software package they buy. Accordingly, if the applications you produce don't 
seem up-to-date, users probably won't be satisfied with your software. One way to 
make your applications more visually appealing is by using attractive graphics, and 
even animation. Unfortunately, animation has become something of a black art within 
programming circles, and many competent programmers avoid it because the realm of 
motion graphics appears to be so complex.

Last month, we introduced you to the TImageList class and demonstrated how it can 
help display non-rectangular bitmaps ("Drawing Non-Rectangular Bitmaps with a 
TImageList"). Delphi 2.0 defines a new version of the TImageList class, which 
encapsulates behavior for the new windows Image List common control. In a future 
issue, we'll discuss the relative merits of the new TImageList class. In this 
article, we'll show how you can use the Delphi 1.0 TImageList class to perform a 
simple type of animation called frame animation.

Animation clarification:

There are two predominant forms of animation that most computer programs currently 
use - frame animation and cast animation. Of the two, cast animation is more 
complex, but also more flexible.

In frame animation, you prepare a series of entire scenes and show those scenes in 
quick succession to give the illusion of movement. This is how cartoon animation 
works and how videotape stores picture information.

In contrast, cast animation separates information about the background from the 
moveable elements. This arrangement allows you to create a small image (called a 
sprite) that moves around on a background, without recording in advance all of the 
possible positions, as you would do with frame animation.

Framed, and enjoying it

Delphi provides several types of components and objects that you'll commonly use to 
display and manipulate graphic images. As you might expect, some of these 
components and objects are very useful for displaying graphics, but most of them 
are inappropriate for such complex tasks as frame animation.

For example, Image components make it simple to display a single bitmap image. 
However, they're not necessarily better for animation than PaintBox components, 
which use the Canvas of their parent forms for drawing purposes.

Similarly, a TBitmap object is useful for storing a single image, but you wouldn't 
want to create a separate TBitmap object for each frame of an animation sequence. 
If you did, you'd need to track every object, each of which requires its own color 
palette, thus wasting memory. (This is particularly true if you're displaying 
256-color bitmaps on a system that has a Super VGA video adapter.)

The TImageList class provides a different set of benefits. Since it's designed to 
manage a set of identically-sized bitmap images, the TImageList class stores 
several images in an internal TBitmap object and, therefore, uses the same palette 
for all of them. As a result, the TImageList class is an ideal core element of 
frame- animation code. For more information on the internal workings of the 
TImageList class, see "How TImageList objects manage bitmaps".

To perform frame animation, you'll add each frame image to a TImageList object. 
Then, you'll use the TImageList object's Draw() method to draw one of the specific 
bitmaps the list contains.

The Draw() method accepts three parameters: the destination Canvas, the horizontal 
and vertical coordinates of the top-left corner within the source image, and the 
index of the source image within the list. By simply changing the value of the 
index parameter, you can choose to draw any of the images the TImageList object 

By the way, when you've finished using a TImageList object, you're responsible for 
releasing its memory by calling its Free method. The only remaining problem is how 
to eliminate some of the all-too-common flicker that sometimes occurs when you call 
the Repaint or Refresh methods.

My friend flicker

If you've ever considered animation programming, you're probably familiar with the 
term double-buffering. If you've never heard the term, don't worry; you're not 

Because it takes time to load an image from a file or compose an image by using 
various graphics operations, you don't want to perform these operations directly 
onscreen. If you do, you'll probably notice a significant amount of flicker in the 
displayed image, since Image and Bitmap components will automatically repaint 
themselves when you modify them.

In double-buffering, you maintain a temporary location - such as a TBitmap object 
that isn't visible - for building the new image you want to display. When you're 
ready to display the image, you can simply use the CopyRect() method of the TBitmap 
class to quickly transfer the information from the invisible TBitmap object 
(typically called an offscreen bitmap) to a PaintBox or Image component. Since the 
CopyRect() method is very fast (relatively speaking), you'll reduce or eliminate 
visible flicker when you update the image onscreen.

Since the TImageList class maintains its own internal TBitmap object (to store all 
the images), it has exactly what we need to store multiple images and double-buffer 
them! Since we'll probably want to use the TImageList class with a PaintBox 
component on a regular basis, let's consider what we'll need to do to combine these 
elements into a single new animation component.

The "Animator"

Since our animation component is primarily a device for displaying a series of 
bitmap images, we'll derive the TAnimator class from the TPaintBox class. By doing 
so, the TAnimator objects will automatically acquire the benefits of a PaintBox 
component, such as being able to use its owner's Canvas instead of having to create 
an additional Canvas of its own.

Within the TAnimator class, we'll need to create a TImageList object to contain all 
the animation images. To simplify the interface for this component, we'll assume 
that any programmer using this component will understand the basics of the 
TImageList class. Accordingly, we'll make this object accessible by creating a 
runtime, read- only property named ImageList, which you can use to access the 
methods of the internal TImageList object.

Since the size of the images you want to display may not be the same as the initial 
size of the animation component, we'll create a ResizeImageList() method. This 
method will destroy the current internal TImageList and create a new one based on 
the size parameters that you pass to the method.

Next, we'll provide a public method named Animate, which tells the animation 
component to advance to the next frame and draw it. When we do so, we must avoid 
embedding a Timer component or any other type of time-related interval code, 
because different applications will have different timing requirements.

For instance, if you're creating animation within an about box, you could place a 
simple timer in the code that counts the number of WM_IDLE messages the application 
receives. In contrast, if your application is a game that will run under Windows 95 
or Windows NT, you may want to trigger the animation sequence using a thread and 
the SleepEx() function or the ThreadedTimer component we showed you how to build 
last month ("Creating a Threaded Timer for Delphi 2.0").

Appropriately, we've added some code that allows you to use this component under 
Delphi 2.0. In a future issue, we'll examine the full capabilities of the new 
TImageList class. For now, recognize that the new version provides all the 
capabilities of the old version if you configure it properly.

Last but not least, we'll provide a runtime, read-only property named CurrentIndex, 
which will identify the image the animation component is currently displaying. Now 
let's build the Animator component. Afterwards, we'll create a simple animation 
form that uses the Animator component to display a series of bitmaps.

Animation preparation

To begin, use the Component Expert dialog box to create a new component source 
file. Enter TAnimator as the Class Name, TPaintBox as the Ancestor Type, and 
DelphiJournal as the Palette Page. Click OK to create the new source file.

When the new source file appears, enter the appropriate code from Listing A. (For 
each listing in this article, we've highlighted in bold the code you'll need to 
enter.) When you finish entering the code, save the file as ANIMATOR.PAS.

1   {Listing A: ANIMATOR.PAS }
3   unit Animator;
5   interface
7   uses
8     SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, 
9   Dialogs, ExtCtrls;
11  type
12    TAnimator = class(TPaintBox)
13    private
14      { Private declarations }
15      FImageList: TImageList;
16      FCurrentIndex: Integer;
17    protected
18      { Protected declarations }
19      procedure Paint; override;
20    public
21      { Public declarations }
22      constructor Create(AOwner: TComponent); override;
23      destructor Destroy; override;
24      procedure Animate;
25      procedure ResizeImageList(X, Y: Integer);
26      property ImageList: TImageList
27        read FImageList;
28      property CurrentIndex: Integer
29        read FCurrentIndex;
30    published
31      { Published declarations }
32    end;
34  procedure register;
36  implementation
38  constructor
39    TAnimator.Create(AOwner: TComponent);
40  begin
41    inherited Create(AOwner);
42  {$IFDEF VER80} {If Delphi 1.x}
43    FImageList := TImageList.Create(Width, Height);
44  {$ELSE} {If Delphi 2.0}
45    FImageList := TImageList.CreateSize(Width, Height);
46    FImageList.Masked := False;
47  {$ENDIF}
48    FCurrentIndex := 0;
49  end;
51  destructor
52    TAnimator.Destroy;
53  begin
54    FImageList.Free;
55    inherited Destroy;
56  end;
58  procedure TAnimator.ResizeImageList(X, Y: Integer);
59  begin
60    FImageList.Free;
61  {$IFDEF VER80} {If Delphi 1.x}
62    FImageList := TImageList.Create(X, Y);
63  {$ELSE} {If Delphi 2.0}
64    FImageList := TImageList.CreateSize(X, Y);
65    FImageList.Masked := False;
66  {$ENDIF}
67  end;
69  procedure TAnimator.Animate;
70  begin
71    Inc(FCurrentIndex);
72    if FCurrentIndex >= FImageList.Count then
73      FCurrentIndex := 0;
74    Paint;
75  end;
77  procedure TAnimator.Paint;
78  begin
79    if FImageList.Count > 0 then
80      FImageList.Draw(Canvas, 0, 0, FCurrentIndex)
81    else
82      inherited Paint;
83  end;
85  procedure register;
86  begin
87    RegisterComponents(`Test', [TAnimator]);
88  end;
90  end.

Next, use the Install Components dialog box to add the ANIMATOR.PAS file to the 
list of Installed Units. Click OK to compile the ANIMATOR.PAS file and link it to 
the new version of the Component Library.

Animation demonstration

To see how the Animator component works, you must first create a new blank form 
project. Then, place a Timer component, an Image component, and an Animator 
component on the form.

Next, create event-handling methods for the form's OnCreate event property and the 
Timer component's OnTimer property. Now enter the appropriate source code from 
Listing B. When you finish, save the form file as ANIMATE.PAS, and save the project 

91  {Listing B: ANIMATE.PAS }
93  unit Animate;
95  interface
97  uses
98    SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms,
99    Dialogs, ExtCtrls, StdCtrls, Buttons, Animator;
101 type
102   TForm1 = class(TForm)
103     Timer1: TTimer;
104     Image1: TImage;
105     Animator1: TAnimator;
106     procedure FormCreate(Sender: TObject);
107     procedure Timer1Timer(Sender: TObject);
108   private
109     { Private declarations }
110     MyList: TImageList;
111     ImageIndex: Integer;
112   public
113     { Public declarations }
114   end;
116 var
117   Form1: TForm1;
119 implementation
121 {$R *.DFM}
123 procedure TForm1.FormCreate(Sender: TObject);
124 var
125   WorkBmp: TBitmap;
126   Offset: Integer;
128   procedure AddImage;
129   begin
130     Image1.Picture.Bitmap.Width := WorkBmp.Width + Offset;
131     Image1.Picture.Bitmap.Canvas.Draw(Offset, 0, WorkBmp);
132     Animator1.ImageList.Add(WorkBmp, nil);
133     Inc(Offset, WorkBmp.Width);
134   end;
136 begin
137   WorkBmp := TBitmap.Create;
138   WorkBmp.LoadFromFile(`C: \delphi\images\buttons\arrow1d.bmp');
139     Offset := 0;
140     Animator1.ResizeImageList(WorkBmp.Width, WorkBmp.Height);
141     AddImage;
142     WorkBmp.LoadFromFile(`C: \delphi\images\buttons\arrow1dl.bmp'
143     AddImage;
144     WorkBmp.LoadFromFile(`C: \delphi\images\buttons\arrow1l.bmp');
145     AddImage;
146     WorkBmp.LoadFromFile(`C: \delphi\images\buttons\arrow1ul.bmp'
147     AddImage;
148     WorkBmp.LoadFromFile(`C: \delphi\images\buttons\arrow1u.bmp');
149     AddImage;
150     WorkBmp.LoadFromFile(`C: \delphi\images\buttons\arrow1ur.bmp'
151     AddImage;
152     WorkBmp.LoadFromFile(`C: \delphi\images\buttons\arrow1r.bmp');
153     AddImage;
154     WorkBmp.LoadFromFile(`C: \delphi\images\buttons\arrow1dr.bmp'
155     AddImage;
156     ImageIndex := 0;
157     WorkBmp.Free;
158 end;
160 procedure TForm1.Timer1Timer(Sender: TObject);
161 begin
162   Animator1.Animate;
163 end;
165 end.

Then, double-click on the Image component, and load one of the button bitmaps from 
the \DELPHI\IMAGES\BUTTONS directory. It doesn't matter which button, since we'll 
replace it with the images of several other button bitmaps.

Now, build and run the application. When the main form appears, you'll notice the 
arrow images spinning slowly in the Animator component's area. Immediately below, 
you'll notice that we display all the different button bitmaps in the Image 
component, as shown in Figure A.

In fact, this is the way the TImageList class stores the bitmap images that it 
draws in the Animator component's area. As the Timer component's interval expires, 
it calls the Animate method of the Animator com-ponent, which, in turn, draws the 
next image from its internal bitmap.


Even simple frame animation can be a complex undertaking if you manage all the display tasks yourself. Fortunately, the TImageList class takes care of many details for us, such as double-buffering image information and storing all the images in a single bitmap. By wrapping a TImageList object and the capabilities of the TPaintBox class together in a new component, as we've shown here, you can easily add an animation sequence to your next Delphi project.

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC