TGif.pas    copyright (C) 2000   R. Collins
rlcollins@ksaits.com

LEGAL STUFF:

This software is provided "as-is".  This software comes without warranty 
or garantee, explicit or implied.  Use this software at your own risk.  
The author will not be liable for any damage to equipment, data, or information
that may result while using this software.

By using this software, you agree to the conditions stated above.

This software may be used freely, provided the author's name and copyright
statement remain a part of the source code.

NOTE:  CompuServe, Inc. holds the patent to the compression algorithym
used in creating a GIF file.  Before you save a GIF file (using LZW
compression) that may be distributed for profit, make sure you understand
the full implications and legal ramifications of using the LZW compression.

THE CODE:

This component was written using Borland's Delphi 3, but should be source-code
compatible with later versions of Delphi.  I cannot state if the code will compile
on Delphi 1 or Delphi 2.  However, since you have the source code, you should be
able to modify it to work on the earlier versions of Delphi.

This component was created using the GIF89a specification from CompuServe.  It will 
read all valid GIF87a and GIF89a versions of GIF files, and write both versions 
(version type is selected automatically).

It is written with the understanding that other programmers will need to read it, 
and possibly modify it for their own specific needs. Comments are meant to be 
descriptive and informative; however, I am assuming an experienced software engineer 
level of experience.  This code is not written for beginners.

The TGif component is a descendant of TGraphic, which means that the user will get 
most of the TGraphic functionality (LoadFromFile, SaveToFile, etc).  However, it 
should be remembered that a GIF file may hold more than one single image (animated 
GIFs may have any number of images); some of the methods and properties of a TGraphic 
assume a single image.  These methods and properties may produce unexpected results.  
Read the interface reference (below) carefully.

All images read in from a GIF file are stored as a list of single-byte pixel values.  
Each pixel value is an index (0..255) into the color table and palette of the image.  
When needed, a TBitmap is created to display the image.

In short, after a GIF is read in, simply reference the BITMAP property of the TGif 
to get a copy of the image. Images are stored in a TList,  indexed 0..(n-1) where n 
is the number of images in the file.  Thus,  the first image from a file (and 
usually the only image) is retrieved by BITMAP[0];

Not all GIF capabilites have been implemented.  In general, these functions are 
either redundant with other features, or somewhat old and out-dated. However, if you 
have a pressing need for one of these functions, and do not know how to implement 
them yourself, e-mail me and I'll see if I can implement them for you.  Be aware: I 
reserve the right to charge a fee for my time (I also reserve the right to work for 
free).  In either case, I work cheap.

Not implemented:

1) TEXT extension blocks have not been implemented.  A TEXT block is a  data block 
   that defines a text string to be written "over" an image. However, these are 
   rarely used, and probably more trouble than they are worth.  

2) The SaveToClipboardFormat and LoadFromClipboardFormat have not been  implemented.  
   For most users, retrieving the BITMAP[n] of an image and saving that to a 
   clipboard will serve their needs better than saving an entire multi-image GIF.  
   (These functions are already provided in the TBitmap interface supplied with 
   Delphi).

3) User input logic is not provided.  The GIF specification allows a state where a 
   GIF will stop rendering at a point, and wait for user input from the keyboard.  
   The specification here is not very well defined, and also seems to be a little 
   out-dated by today's standards.  This capability can be simulated using the
   OnProgress event to determine when to ask the user for input.

A special feature has been added: you can save a GIF file without using the
patented LZW compression scheme.  Use the property LZWSize to set the minimum bit 
code size to 0; then when saving the GIF, the pixels will simply be packed and 
written without any compression at all.  This module can read this file back in, but 
it is not expected that any other GIF decoder will be able to read the data.  
However, by avoiding the LZW compression, the patent owners will not object to your 
files.

KNOWN PROBLEMS:

Besides the unimplemented features above, the TGif component works well.

The LZW decode/encode loop is slower than I would like; it could be made
faster by using embedded assembly code.  There may be a better algorithym
for decode/encode that is faster than what is included here.

The component makes use of allocated/deallocted memory blocks.  An exception
(abort error code) could result in dangling pointers, and a memory leak.

Converting bitmaps with more than 256 colors to 8-bit mode is simplistic.
The code keeps only the most significant 8 bits of color information (see
the MASK in MakeBitmaps); a better approach would be to count and sort the 
colors of a bitmap, and use the most important 256 colors.  But this is slow 
and clumsy, and I did not believe it really belonged in this module.  For 
better color conversion, use a separate package to reduce colors to 256 before 
saving it as a GIF file.

To keep a uniform interface for 8-bit, 4-bit, 2-bit, and 1-bit bitmaps,
the TBitmap.CANVAS.PIXELS[x,y] property is used.  Unfortunately, this
is slower than other approaches (e.g. SCANLINEs).  This should be optimized.

The AddBitmap procedure creates a new color table for each image, even though
it is possible that all bitmaps may be able to use the same color table.  This
should be optimized.

Counting unique colors is slow and clumsy.  If anyone has a faster way of doing
this, I would appreciate some input.

INTERFACE

constructor Create;
	Creates a new empty GIF component.

constructor CreateFromBitmap(Bitmap: TBitmap);
	Creates a new GIF component, and defines the first image from the
	given bitmap.  This is the easiest way to convert a bitmap to a
	GIF.

procedure Assign(Source: TPersistent);
	Copies the SOURCE to the GIF component.  The old contents of the GIF
	are deleted and lost before the copy.  The SOURCE must be either a
	TBitmap or a TGif.

procedure Free;
	Deletes all data from the GIF, and returns the memory to the system
	for other uses.  The GIF component should not be used again unlesss
	it is re-created by CREATE.

procedure FreeImage;
	Deletes the image data from the GIF, but does not release the 
	component.

procedure LoadFromStream(Source: TStream);
	Reads and decodes a GIF specification from a stream.  After the GIF 
	is read in and decoded, each individual image may be accessed using
	the BITMAP property.

procedure SaveToStream(Destination: TStream);
	Encodes and writes the images of a GIF to a stream.

procedure LoadFromFile(Filename: string);
	(Provided by TGraphic).  Reads a GIF from a file.  See LoadFromStream.

procedure SaveToFile(Filename: string);
	(Provided by TGraphic). Saves a GIF to a file.  See SaveToStream.

procedure SetCanvas(Canvas: TCanvas; Rect: TRect);
	An alternative to the BITMAP property.  This allows the user to specify
	a canvas on which to draw the image read from a GIF file.  The RECT
	argument specifies the area of the CANVAS to draw upon.  The image is
	drawn using the StretchDraw method; meaning the image will fill the
	entire RECT area, even if it is distorted in the drawing process.

procedure DrawImage(Image: integer);
	If a canvas has been defined with SetCanvas, draws the specified image
	on the canvas within the given rectanular area.  Images are numbered
	0..(n-1), where n is the number of images in the GIF file.  For a GIF
	with a single image, DrawImage(0) draws the (only) image on the canvas.
	
procedure DrawBackground;
	If a canvas has been defined with SetCanvas, and if the GIF specified
	a background color for the GIF images, this routine will fill the
	given rectangular area with the GIF background color.

procedure AnimateImage(Count: integer);
	If a canvas has been defined with SetCanvas, and if the GIF is an
	animated GIF (meaning it has more than one image), this will display
	the images in sequence on the canvas.  The Count is the number of
	times to show the series of images.  A Count of (-1) will cause the
	images to repeat endlessly, untill stopped by calling AnimateImage
	with a count of 0 (zero).  The images are displayed by a call to
	a TTimer object, so that calling AnimateImage does not cause the
	program to "hang" at this point.  AnimateImage will return immediately,
	and the image animation will occur in the background.

procedure AddBitmap(bitmap: TBitmap);
	Adds a new image to the GIF, using BITMAP to create the image data.
	This routine creates a new image record and color table, appending
	them to the list of previously defined images.  An animated GIF file
	may be created by repeatedly calling AddBitmap with the next image
	in the sequence.

property Tag: integer;
	(read write)
	(Provided by TGraphic) Defines a simple data space that the user may use as
	needed.  Typically used to provide a simple programmable ID for a component.

property Signature: string;
	(read only)
	Returns the signature of the GIF read from the GIF file, "GIF89a" or "GIF87a".
	For new GIFs created by the user and not yet written to a file, this
	value is "------".  The signature in these cases will be determined at
	the time the GIF is saved to a file.

property ScreenDescriptor: PGifScreenDescriptor;
	(read only)
	The pointer value is read-only, although the contents of the descriptor record
	may be changed by the user.  This is not recommended unless you have a very
	good understanding of the GIF format.  Changing the fields of the descriptor
	record may cause the module to write an invalid GIF file.

property ImageCount: integer;
	(read only)
	Returns the number of images defined for the GIF.  This includes images read
	from a GIF file, as well as new images created with AddBitmap.

property ImageDescriptor[Image: integer]: PGifImageDescriptor;
	(read only)
	The pointer value is read-only, although the contents of the descriptor record
	may be changed by the user.  This is not recommended unless you have a very
	good understanding of the GIF format.  Changing the fields of the descriptor
	record may cause the module to write an invalid GIF file.

property Bitmap[Image: integer]: TBitmap;
	(read only)
	Returns the bitmap for a specific image contained in the GIF.  The handle to
	the bitmap is read-only, therefore a new bitmap may not be assigned for a
	given image.  However, changes may be made to the bitmap (such as DRAW), but
	thse changes are "local" only, and will not be saved if the GIF is written
	to a file.

property ColorTableCount: integer;
	(read only)
	Returns the number of color tables currently defined for the GIF.  This is
	typically one color table per image, but it is possible to define color
	tables that are not used by any image, or for images to share a "global"
	color table.

property ColorTable[Table: integer]: PGifColorTable;
	(read only)
	The pointer value is read-only, although the contents of the color table record
	may be changed by the user.  This is not recommended unless you have a very
	good understanding of the GIF format.  Changing the fields of the color table
	record may cause the module to write an invalid GIF file.

property Height: integer;
property Width: integer;
	(read write)
	These values only change the size of the default screen defined in the
	ScreenDescriptor record.  They do not change the dimensions of any image 
	definition or image bitmap.  To change the dimension of an image, retrieve the 
	bitmap for that image, use StretchDraw to re-draw the image to a new bitmap
	with the proper dimensions, then use AddBitmap to save the image in a new
	GIF object.

property ImageDelay: integer;
	(read write)
	Returns or sets the default animation delay for each image.  The animation 
	delay is the amount of time (in 1/100 seconds) that each image will be
	displayed before the next image is displayed.

property Interlaced: boolean;
	(read write)
	Determines whether images are saved in a GIF file with interlaced or "flat"
	scanline order.  Setting interlaced to TRUE is normally used when an image
	is displayed as it is decoded (see ProgressiveDisplay), to provide more 
	pleasing and informative partial image.

property LZWSize: integer;
	(read write)
	Returns or defines the minimum bit size for LZW compression.  Valid range
	is 2..9 bits, and is normally set to be the same as the number of bits used
	to define a single color.  That is, a 256-color image should have LZWSize set
	to 8, while an image using 2 or 4 colors would use an LZWSize of 2.  Note:
	setting LZWSize to 0 will disable LZW compression completely.  Images saved
	with this format may be read again by this component (TGif), but will probably
	not be read by other GIF image decoders.

property ColorsUsed[Image: integer]: integer;
	(read only)
	For a given image, returns the number of unique colors found in the image.
	This number is not necessarily the same as the size of the color table (or
	the image palette), since it is possible to define several color entries to
	be identical.  This routine is slow and clumsy, and no optimization has been
	performed on it.  Do not use it in time-critical applicaitons.

property Animating: boolean;
	(read only)
	Returns TRUE if an multi-image GIF is currently cycling through the list of
	images, displaying them on a canvas.

property Empty: boolean;
	(read only)
	Returns TRUE if no images are defined in the GIF.

property ProgressiveDisplay:boolean;
	(read write)
	Set this property to TRUE (and define a canvas using SetCanvas) if you wish
	an image to be displayed while it is being decoded.  Setting this to TRUE will
	give the viewer a partial image as it is being read in, but will significantly
	slow down the decoding process.

property Transparent: boolean;
	(read write)
	Returns or defines if a GIF is transparent (see TBitmap.Transparent in the Delphi
	help files).  For a GIF with multiple images, this property sets the transparency
	for all images to TRUE or FALSE.  If set to TRUE, the transparent color is
	automatically set to be the same as the lower-left corner of each image.  To change
	the transparent color, see property TransparentColor.

property ColorIndex[Image, X, Y: integer]: integer;
	(read write)
	Returns or sets the color index (the index into the image color table) for a
	single pixel of the image.  The color index must be in the range 0..255.

property Color[Image, X, Y: integer]: TColor;
	(read write)
	Returns or sets the color of a single pixel of the specified image.  The color
	must exist in the image color table.

property TransparentIndex[Image: integer]:  integer;
	(read write)
	For a given image, returns the index (into the image color table) of the color
	used as the transparent color.  If the image is not defined to be transparent,
	this value has no meaning.

property TransparentColor: TColor;
	(read write)
	Returns or defines the transparent color to be used for all images.  The color
	must exist in the palette of each image in the GIF.

property Palette: HPaletteread;
	(read only)
	Returns the handle of the palette associated with the GIF global color table.
	This color table may or may not be used by any image in the GIF.  This property
	is provided mainly for compitibility with TGraphic.

property ImageWidth[Image: integer]: integer;
property ImageHeight[Image: integer]: integer;
property ImageDepth[Image: integer]: integer;
	(read only)
	Returns the size of the specified image.


event OnProgress: TProgressEvent;
	TProgressEvent = procedure (Sender: TObject; Stage: TProgressStage; 
	PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string) of object;
	OnProgress is called as a GIF is decoded, encoded, or displayed.  Only the
	Sender, Stage, PercentDone, and Msg parameters are used.  See SetCanvas and
	ProgressiveDisplay to display partial images as they are decoded.


