2006. március 24., péntek

How to store several bitmaps into a single file


Problem/Question/Abstract:

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.

Nincsenek megjegyzések:

Megjegyzés küldése