Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: How to save and read muliple bitmaps in a TStream


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 3 - Last Post: Nov 6, 2017 7:43 PM Last Post By: Bob Rasmusens
Bob Rasmusens

Posts: 10
Registered: 5/18/00
How to save and read muliple bitmaps in a TStream  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 6, 2017 6:30 PM
Rad Studio XE 10.1
I am creating a configuration file which contains among other things user selected bitmaps to be used when that user is using the program.
I am trying to save multiple bitmaps to a stream with intervening information into a a single file for later retrieval. However it looks like the first bitmap.LoadFromStream reads the complete file causing a stream read error on the following read. I would expect, perhaps incorrectly, that bitmap.LoadFromStream would only read in enough information to satisfy the bitmap!!

Is this normal Behavior?
Is there another way to save multiple bitmaps to a file?

The following stripped down code illustrates.

 ..
  stream := TMemoryStream.Create;
  BitmapOut1.LoadFromFile("Test.bmp");
  BitmapOut2.LoadFromFile("Test2.bmp");
  AValue    := 666;
  
    // Output two bitmaps with value in between
    BitmapOut1.SaveToStream(stream);
    stream.WriteBuffer(AValue, SizeOf(AValue));
    BitmapOut2.SaveToStream(stream);
 
    // Reset stream to start position 
    stream.Position := 0; 
    // Clear Value
    AValue           := 0;
    // Read in first bitmap loads the bit map and looks fine
    Bitmap1.LoadFromStream(stream);
    // At this point the stream.position is equal to stream.size and the following line generates a "Stream read error'
    stream.ReadBuffer(AValue, SizeOf(AValue));
    Bitmap2.LoadFromStream(stream);
...
  
Eli M

Posts: 1,346
Registered: 11/9/13
Re: How to save and read muliple bitmaps in a TStream  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 6, 2017 7:21 PM   in response to: Bob Rasmusens in response to: Bob Rasmusens
Use a TFDMemTable. Save the images 1 per row in a blob field. TFDMemTable.SaveToFile().

Can also do this with SQLite.

Also check out this:
http://www.fmxexpress.com/video-streaming-using-motion-jpeg-with-delphi-xe5-firemonkey-on-windows-and-ios/
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: How to save and read muliple bitmaps in a TStream  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 6, 2017 7:29 PM   in response to: Bob Rasmusens in response to: Bob Rasmusens
Bob Rasmusens wrote:

However it looks like the first bitmap.LoadFromStream reads the
complete file causing a stream read error on the following read.

If the input stream's Position is 0, TBitmap.LoadFromStream() might
read only enough bytes to load a single image, leaving the new Position
at the end of the image data. However, this is NOT guaranteed.

If the input stream's Position is > 0, TBitmap.LoadFromStream() reads
the entire input stream from its current Position to its end to a
temp TMemoryStream, then loads a single image from that copy. It makes
no attempts to set the new Position of the input stream to the end of
the image data.

Even if the input stream's initial Position is 0, this copy may still
happen! For example, on Windows, if GDI+ is used for graphics, the
input stream is used as-is and only the actual image bytes are read.
But, if DirectX is used instead, a copy is made and then the image is
loaded from that copy. This same copy logic happens on OSX, too.

This is not the case in VCL, where TBitmap.LoadFromStream() does not
read more than it actually needs, regardless of the input stream's
initial Position.

I would expect, perhaps incorrectly, that bitmap.LoadFromStream would
only read in enough information to satisfy the bitmap!!

Only when the initial Position of the input stream is 0, if even that.

The reason the temp stream is created at all is because TBitmap in FMX
supports multiple image types, and FMX's underlying graphics system
that TBitmap.LoadFromStream() calls into requires the source stream's
initial Position to be 0. TBitmap.LoadFromStream() does not know what
kind of image data is being loaded, and so does not attempt to
calculate the actual image size when making copies.

Is this normal Behavior?

Yes, sadly.

Is there another way to save multiple bitmaps to a file?

When writing your bitmaps to your output stream, use an intermediate
stream to encode the bitmap, and then write that image's Size to the
output stream before writing the image data, eg:

BitmapOut1.LoadFromFile("Test.bmp");
BitmapOut2.LoadFromFile("Test2.bmp");
..
stream := ...; // TMemoryStream, TFileStream, whatever...
try
  // Output two bitmaps with value in between
 
  TempStream := TMemoryStream.Create;
  try
    BitmapOut1.SaveToStream(TempStream);
    TempStream.Position := 0;
    ImageSize := TempStream.Size;
    stream.WriteBuffer(ImageSize, SizeOf(ImageSize));
    if ImageSize > 0 then
      stream.CopyFrom(TempStream, ImageSize);
 
    TempStream.Clear;
 
    AValue := 666;
    stream.WriteBuffer(AValue, SizeOf(AValue));
 
    BitmapOut2.SaveToStream(TempStream);
    TempStream.Position := 0;
    ImageSize := TempStream.Size;
    stream.WriteBuffer(ImageSize, SizeOf(ImageSize));
    if ImageSize > 0 then
      stream.CopyFrom(TempStream, ImageSize);
  finally
    TempStream.Free;
  end;
  ...
finally
  stream.Free;
end


This way, when you are reading bitmaps back from an input stream, you
can use that extra Size value to read only the portions that contain
image data:

...
stream.Position := 0; 
AValue := 0;
 
TempStream := TMemoryStream.Create;
try
  stream.ReadBuffer(ImageSize, SizeOf(ImageSize));
  if ImageSize > 0 then
    TempStream.CopyFrom(stream, ImageSize);
  Bitmap1.LoadFromStream(TempStream);
 
  TempStream.Clear;
 
  stream.ReadBuffer(AValue, SizeOf(AValue));
 
  stream.ReadBuffer(ImageSize, SizeOf(ImageSize));
  if ImageSize > 0 then
    TempStream.CopyFrom(stream, ImageSize);
  Bitmap2.LoadFromStream(TempStream);
finally
  TempStream.Free;
end;
...


For easier use, you could optionally wrap the extra code in a class
helper, eg:

type
  TMyStreamHelper = helper class for TStream
  public
    procedure ReadBitmap(Bitmap: TBitmap);
    procedure WriteBitmap(Bitmap: TBitmap);
  end;
 
procedure TMyStreamHelper.ReadBitmap(Bitmap: TBitmap);
var
  TempStream: TMemoryStream;
  ImageSize: Integer;
begin
  TempStream := TMemoryStream.Create;
  try
    Self.ReadBuffer(ImageSize, SizeOf(ImageSize));
    if ImageSize > 0 then
      TempStream.CopyFrom(Self, ImageSize);
    Bitmap.LoadFromStream(TempStream);
  finally
    TempStream.Free;
  end;
end;
 
procedure TMyStreamHelper.WriteBitmap(Bitmap: TBitmap);
var
  TempStream: TMemoryStream;
  ImageSize: Integer;
begin
  TempStream := TMemoryStream;
  try
    Bitmap.SaveToStream(TempStream);
    TempStream.Position := 0;
    ImageSize := TempStream.Size;
    Self.WriteBuffer(ImageSize, SizeOf(ImageSize));
    if ImageSize > 0 then
      Self.CopyFrom(TempStream, ImageSize);
  finally
    TempStream.Free;
  end;
end;


And then you can use it like this:

BitmapOut1.LoadFromFile("Test.bmp");
BitmapOut2.LoadFromFile("Test2.bmp");
 
stream := ...; // TMemoryStream, TFileStream, whatever ...
AValue := 666;
  
// Output two bitmaps with value in between
stream.WriteBitmap(BitmapOut1);
stream.WriteBuffer(AValue, SizeOf(AValue));
stream.WriteBitmap(BitmapOut2);
 
...
 
stream.Position := 0;
AValue := 0;
 
stream.ReadBitmap(Bitmap1);
stream.ReadBuffer(AValue, SizeOf(AValue));
stream.ReadBitmap(Bitmap2);
...


--
Remy Lebeau (TeamB)
Bob Rasmusens

Posts: 10
Registered: 5/18/00
Re: How to save and read muliple bitmaps in a TStream  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 6, 2017 7:43 PM   in response to: Bob Rasmusens in response to: Bob Rasmusens
Remy your answer confirmed what I thought and your solution was along the lines of what I was thinking.
Thank-you
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02