Title: How to store several bitmaps into a single file?
Question: Is there a simple way to write a TBitmap object to a file and read it back?
I want to store bitmaps and other data all in one file (much like word processors are capable of doing).
Answer:
If you wish to store multiple things into a file, you'll need to implement some
sort of file structure so you can know what and where things are in the file.
For example, if you wished to store several bitmaps to a file, you could structure
your file like this:
file header
bitmap count
bitmap header
bitmap size
bitmap stream
bitmap trailer
...
file trailer
Where "file header" contains information such as the version of the file and a
unique file structure identifier, "bitmap count" is the number of bitmaps saved
to the file, "bitmap header" is a unique identifier which indicates the start
of a bitmap entry in the file, "bitmap size" is the size of the bitmap stream,
"bitmap stream" is the bitmap's stream (from SaveToStream), "bitmap trailer"
is a trailer identifier which indicates the end of the bitmap entry, and
"file trailer" is a unique identifier which indicates the end of the file,
and optionally contains the size of the file and a CRC of the file (for error
detection). Of course, you'd iterate the "bitmap header"..."bitmap trailer"
structure once per bitmap saved to the file.
You can use a TFileStream to read / write this structure. You'll need to write
a number of methods which read and interpret each section. You'll also want to
create a TBitmap instance each time you encounter a "bitmap header" structure.
Here's a quick example of how to implement the "bitmap header"..."bitmap trailer"
section:
const
BITMAP_HEADER = 100;
BITMAP_TRAILER = 200;
procedure SaveBitmap(Bitmap: TBitmap; Stream: TStream);
var
Buffer: TMemoryStream;
Identifier: LongInt;
Size: LongInt;
begin
Buffer := TMemoryStream.Create;
try
Bitmap.SaveToStream(Buffer);
Identifier := BITMAP_HEADER;
Stream.Write(Identifier, SizeOf(Identifier));
Size := Buffer.Size;
Stream.Write(Size, SizeOf(Size));
Buffer.Position := 0;
Stream.CopyFrom(Buffer, Size);
Identifier := BITMAP_TRAILER;
Stream.Write(Identifier, SizeOf(Identifier));
finally
Buffer.Free;
end;
end;
procedure ReadBitmap(Bitmap: TBitmap; Stream: TStream);
var
Buffer: TMemoryStream;
Identifier: LongInt;
Size: LongInt;
begin
Buffer := TMemoryStream.Create;
try
Stream.Read(Identifier, SizeOf(Identifier));
if Identifier BITMAP_HEADER then
raise Exception.Create( 'Bitmap header expected' );
Stream.Read(Size, SizeOf(Size));
Buffer.CopyFrom(Stream, Size);
Bitmap.LoadFromStream(Buffer);
Stream.Read(Identifier, SizeOf(Identifier));
if Identifier BITMAP_TRAILER then
raise Exception.Create( 'Bitmap trailer expected' );
finally
Buffer.Free;
end;
end;
Of course, you'll need to write other methods to read the other file sections,
and you'll need to call ReadBitmap the correct number of times (specified in
"bitmap count") with a TBitmap instance.