delphi

Save an image and other data to a file in delphi


I have an image, a string, and an integer that i need to save/read as a file. What would be the best way to do this. I was thinking a TStream, but I am unsure of how to implement it.


Solution

  • You could use Delphi's built-in component streaming support and save it either as binary or text data. One disadvantage with this method is if your images are high-resolution then you might be punished in greater file-size when dumping the in-memory-pixel-data to disk compared to the image's original file-format, e.g. jpeg. In my example below I derive a class from TImage and add a string value, MyString and an integer, MyInteger and save as binary data.

    Stream Component to/from a binary-file

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;
    
    type
      TMyImage = class(TImage)
      private
        FString : string;
        FInteger : integer;
      public
    
      published
        property MyString: string read FString write FString;
        property MyInteger: Integer read FInteger write FInteger;
      end;
    
      TFormMain = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        FMyImage : TMyImage;
        FMyImage2 : TMyImage;
      public
    
      end;
    
    var
      FormMain: TFormMain;
    
    implementation
    
    {$R *.dfm}    
    
    procedure WriteComponent(Component: TComponent; Filename : String);
    var
      m :TMemoryStream;    
    begin
      m := TMemoryStream.Create;
      try
        m.WriteComponent(Component);
        m.Seek(0, soFromBeginning);
        m.SaveToFile(Filename);
      finally
        m.Free;
      end;
    end;
    
    function ReadComponent(Filename : String): TComponent;
    var
      m :TMemoryStream;    
    begin
      m := TMemoryStream.Create;
      try
        m.LoadFromFile(FileName);
        m.Seek(0, soFromBeginning);
        result := m.ReadComponent(nil);
      finally
        m.Free;
      end;
    end;
    
    procedure TFormMain.FormCreate(Sender: TObject);
    begin
      FMyImage := TMyImage.Create(Self);
    
      FMyImage.Picture.LoadFromFile('C:\...\Delphi\Common Images\ALIGNPAL.BMP');
      FMyImage.MyString := 'Hello World';
      FMyImage.MyInteger := 12345;
    
      WriteComponent(FMyImage, 'c:\test.dat');
      FMyImage2 := ReadComponent('c:\test.dat') as TMyImage;
    end;
    
    initialization
    
     RegisterClass(TMyImage);
    
    end.
    

    A simplified approach

    If you want to get by with only one String and one Integer than you could use TImage's Caption and Tag properties.

    ...
    FImage : TImage;
    FImage2 : TImage;
    ....
    
    procedure TFormMain.FormCreate(Sender: TObject);
    begin
      FImage := TImage.Create(Self);
    
      FImage.Picture.LoadFromFile('C:\...\Delphi\Common Images\ALIGNPAL.BMP');
      FImage.Caption := 'Hello World';
      FImage.Tag := 12345;
    
      WriteComponent(FImage, 'c:\test.dat');
    
      FImage2 := ReadComponent('c:\test.dat') as TImage;
    end;
    

    Stream Component to/from text

    Below I'v also added useful helper functions, found here, if you want more "readable" data-files by making string representations of the binary-data. File size will however increase.

    function StringToComponentProc(Value: string): TComponent;
    var
      StrStream:TStringStream;
      BinStream: TMemoryStream;
    begin
      StrStream := TStringStream.Create(Value);
      try
        BinStream := TMemoryStream.Create;
        try
          ObjectTextToBinary(StrStream, BinStream);
          BinStream.Seek(0, soFromBeginning);
          Result:= BinStream.ReadComponent(nil);
        finally
          BinStream.Free;
        end;
      finally
        StrStream.Free;
      end;
    end;
    
    function ComponentToStringProc(Component: TComponent): string;
    var
      BinStream:TMemoryStream;
      StrStream: TStringStream;
      s: string;
    begin
      BinStream := TMemoryStream.Create;
      try
        StrStream := TStringStream.Create(s);
        try
          BinStream.WriteComponent(Component);
          BinStream.Seek(0, soFromBeginning);
          ObjectBinaryToText(BinStream, StrStream);
          StrStream.Seek(0, soFromBeginning);
          Result:= StrStream.DataString;
        finally
          StrStream.Free;
        end;
      finally
        BinStream.Free
      end;
    end;