• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 230
  • Last Modified:

Save more than 1 bitmap in one file

How to save more images in one file so that I can it open again and work with each image individually again?
About 16 pictures, 30*30 pixels
0
membername86
Asked:
membername86
  • 11
  • 8
  • 8
  • +7
1 Solution
 
lister07Commented:
You can use a RES file. You can use the in built graphics editor in Delphi to generate these. (Tools|Image Editor) Once you have a RES File. Use a TImageList and the method FileLoad.

function FileLoad(ResType: TResType; Name: string; MaskColor: TColor): Boolean;

At the top of the unit you need to declare a resource file something like

{$R mypictures.res}

(Goes below the form declaration).
0
 
Lukasz LachCommented:
or if you realy want to use files your bitmap won't act as bitmap any more, so that you won't be able to open that file from nay graphic editor etc.
If you realy want that anywany use BlockRead and BlockWrite to read content of the first/second/... bitmap and save it to another file. You will also have to write saved bytes count to know how much bytes program should read. However when saving always bitmaps with the same size/color depth the size is always the same :-)
0
 
SteveWaiteCommented:
listening
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
msa2003Commented:
Windows bitmap format (BMP) does not allow to store more then one image per file, but you could use TBitmap.SaveToStream and TBitmap.LoadFromStream with file streams to store more than one image in the file. The sample below first stores two bitmaps in a one file, then loads them again so that images will interchange:

procedure TForm1.Button1Click(Sender: TObject);
var
  F: TFileStream;
begin
  F := TFileStream.Create('multi.bmp', fmCreate);
  Image1.Picture.Bitmap.SaveToStream(F);
  Image2.Picture.Bitmap.SaveToStream(F);
  F.Free;

  F := TFileStream.Create('multi.bmp', fmOpenRead);
  Image2.Picture.Bitmap.LoadFromStream(F);
  Image1.Picture.Bitmap.LoadFromStream(F);
  F.Free;
end;

Windows Paint will load only the first image ;)
0
 
msa2003Commented:
P. S. Any offsets within the stream are calculated from the bitmap headers. You may only perform sequential read this way. If you need a random read you would use TFileStream.Seek() method in reading stream. Use TFileStream.Position to remember positions in writing stream (actually positions in the file) while storing pictures. I. e.:

F := TFileStream.Create('multi.bmp', fmCreate);
Image1.Picture.Bitmap.SaveToStream(F);
Image2Offset := F.Position;
Image2.Picture.Bitmap.SaveToStream(F);
F.Free;

F := TFileStream.Create('multi.bmp', fmOpenRead);
F.Seek(Image2Offset, soFromBeginning);
Image1.Picture.Bitmap.LoadFromStream(F);
F.Free;

This example will replace Image1 picture with Image2.
0
 
esoftbgCommented:
In the question is not specified type of the file for saving images. If you like to store images into a  database file you can do that. There is no problem about the number and size of pictures and its individual editing, replacing or deleting ...
0
 
msa2003Commented:
I think the way with database to be too complex. Database files are often huge, because they contains much metainformation. This way also needs database support un an application, which will enlarge it approximately by 500 KBytes. Database way is slower than direct file access and requires additional components which are to be installed with a program (i. e. BDE, MDAC, so on). Of course you may create your own TDataSet descendant which will not need BDE or MDAC, but it is a hard way of how to solve an easy problem ;).

I guess that 30*30 pixel pictures might be BMP ;) In order to use other formats just replace TBitmap with corresponding class having SaveToStream and LoadToStream methods such as TJPEGImage (for JPEG), TIcon (for window icons) or even TMetafile (for vector metafiles).
0
 
Slick812Commented:
hello  membername86, You can make files that store most any thing that has bytes, and put many Bitmaps into one file (or many Records, Bitmaps, Icons, wav files, ect) it's a matter of accounting, recording what and where the Data (bytes) are in the file. .  I can show some code for making a Multi bitmap file, but it takes some code, and is not that easy, ask if you want to see some code
0
 
msa2003Commented:
You could also store images as an arrays of bytes using BlockWrite and read them using BlockRead.
0
 
Slick812Commented:
here is some code that will save an Array of Bitmaps to a single file and then Read All of the bitmaps back out of that file. . . I have really simplified this, and it just adds the Bitmap size as an Integer before each bitmap, I normally would add a File header with the size and file position for each bitmap. . .


  private
    { Private declarations }
    AryBmp1: Array of TBitmap;


procedure TForm1.button_CreateBmpsClick(Sender: TObject);
var
i: Integer;
begin
{this button click just creates 3 bitmaps into an Array
you can add as many Bitmaps as you need}
SetLength(AryBmp1, 2);
AryBmp1[0] := TBitmap.Create;
AryBmp1[0].Canvas.Brush.Color := clRed;
AryBmp1[0].Width := 30;
AryBmp1[0].Height := 30;

AryBmp1[1] := TBitmap.Create;
AryBmp1[1].Canvas.Brush.Color := clBlue;
AryBmp1[1].Width := 30;
AryBmp1[1].Height := 30;

SetLength(AryBmp1,Length(AryBmp1)+1);
AryBmp1[2] := TBitmap.Create;
AryBmp1[2].Canvas.Brush.Color := clGreen;
AryBmp1[2].Width := 30;
AryBmp1[2].Height := 30;

for i := 0 to High(AryBmp1) do
Canvas.Draw(i*30,8, AryBmp1[i]);
end;



procedure TForm1.button_SaveBmpAry2FileClick(Sender: TObject);
var
FileStream1 : TFileStream;
BmpStream: TMemoryStream;
NumBmps, IDver: Integer;
FileName: String;
begin
{This Button Click saves the AryBmp1 to File.
it is IMPORTANT to save the bitmap file size in the
new Muti file}
if (Length(AryBmp1) < 1) then
  begin
  ShowMessage('No Images are loaded yet, can not save file');
  Exit
  end;

FileName := 'E:\BmpFile.mbf';
NumBmps := Length(AryBmp1);
DeleteFile(FileName);
IDver := 89898901;
{all file headers should have an Identifier and version,
  I combine these into an integer with assorted digits and
  the last 2 digits as the version number, you could use Asci
  text as an identifier}
FileStream1 := TFileStream.Create(FileName, fmCreate or fmOpenWrite or fmShareDenyWrite);
BmpStream := TMemoryStream.Create;
  {in order to get the size of an Image's file bytes, we'll put it in a
  memory stream, which is created here}
  try
{after each Write to the FileStream1 the file position is
automatically moved to the end of the write}
FileStream1.Write(IDver, SizeOf(Integer));
    {first 4 bytes of file header is the ID integer}
FileStream1.Write(NumBmps, SizeOf(Integer));
    {next 4 bytes of file header is the Number of Bitmaps}
for IDver := 0 to High(AryBmp1) do
  begin
  AryBmp1[IDver].SaveToStream(BmpStream);
  {save each image to the Memory stream}
  NumBmps := BmpStream.Size;
  {it is very important to get the size of bitmap bytes in NumBmps, or
      you will not be able to get the image out of the file}
  FileStream1.Write(NumBmps, SizeOf(Integer));
  BmpStream.Position := 0;
  BmpStream.SaveToStream(FileStream1);
  BmpStream.Clear;
  {it is important to Clear the Stream or the images
      are added to the previous image}
  end;

finally
FreeAndNil(FileStream1);
FreeAndNil(BmpStream);
end;
end;

procedure TForm1.sbut_GetMBmpFileClick(Sender: TObject);
var
FileStream1 : TFileStream;
BmpStream: TMemoryStream;
NumBmps, IDver: Integer;
FileName: String;
begin
{this button click loads the 3 bitmaps from the multi file}
FileName := 'E:\BmpFile.mbf';
if Not FileExists(FileName) then
  begin
  ShowMessage('File does not Exist, can not load');
  Exit;
  end;
if (Length(AryBmp1) > 0) then
  begin
  for IDver := 0 to High(AryBmp1) do
    AryBmp1[IDver].Free;
  ShowMessage('ALL Bmps in Array will be Replaced');
  end;

FileStream1 := TFileStream.Create(FileName,fmOpenRead or fmShareDenyWrite);
BmpStream := TMemoryStream.Create;
try
FileStream1.Read(IDver, SizeOf(Integer));
if IDver <> 89898901 then
  begin
{ALWAYS check your ID, if you try to load a file with incorrect data
BAD THINGS can happen}
  ShowMessage('This is NOT a valid MBF  file for this version, can not load');
  Exit;
  end;

FileStream1.Read(NumBmps, SizeOf(Integer));
SetLength(AryBmp1, NumBmps);
for IDver := 0 to High(AryBmp1) do
  begin
  AryBmp1[IDver] := TBitmap.Create;
  FileStream1.Read(NumBmps, SizeOf(Integer));
{you absolutely NEED the size in Bytes of your Data chunk of a File,
this size will be read into NumBmps}
  BmpStream.CopyFrom(FileStream1, NumBmps);
  {copy the BmpStream from the FileStream, you need to do this
       because the any Graphics LoadFromStream will need a stream that ONLY
       has that graphic in it, remember that the BmpStream position
       is now at the END of the Stream}
  BmpStream.Position := 0;
  AryBmp1[IDver].LoadFromStream(BmpStream);
  BmpStream.Clear;
  end;
finally
FreeAndNil(FileStream1);
FreeAndNil(BmpStream);
end;

for IDver := 0 to High(AryBmp1) do
Canvas.Draw(IDver*30,48, AryBmp1[IDver]);
end;
0
 
GwenaCommented:
There are routines in my ExeMod.pas unit that make it easy to add,delete,replace,search,load..etc any sort of file inside of an external file... or even inside the exe's own file.  You save and load the data by name... you need pay no attention to things like filelengths or where the data is inside the data file.

The following button handler snippet will let you click a button and add a bmp to a data file named MyBMPs.dat that is in the same dir as the exe. The bmp's name in the dat file will be the same as it's filename.

If a bmp has already been stored under this name it will be replaced in the file with the new version..

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
Temp: String;
begin
OpenDialog1.Execute;
Temp := File2String(OpenDialog1.FileName);
SetCurrentDir(ExtractFilePath(Application.ExeName));
InsOrReplaceInFile(ExtractFileName(OpenDialog1.FileName),'MyBMPs.dat',Temp);
end;


This button handler will load a bmp from the MyBMPs.dat file and display it.  The name of the bmp to display is entered into an edit box

procedure TForm1.SpeedButton2Click(Sender: TObject);
var
Temp: String;
BMP: TMemoryStream;
begin
If Edit1.Text <> '' then
begin
  SetCurrentDir(ExtractFilePath(Application.ExeName));
  ExtractFromFile(Edit1.Text,'MyBMPs.dat',Temp);
  Bmp := TMemoryStream.Create;
  String2Stream(Temp,BMP);
  Image1.Picture.Graphic.LoadFromStream(BMP);
  BMP.Free;
end;
end;

once you have your BMP in ram as either a string or stream you can do whatever you like with the data and easily save the modified version back to the .dat file...

If this looks useful I can send you the full source for this qwik-n-dirty demo along with a compiled exe and the latest version of ExeMod.pas

Exactly what sort of program are you writing? what sort of small bmps are going to be stored?

:-)
0
 
membername86Author Commented:
O.K. i`ll check the source, but the thing is that I don`t wanna open the file in paint or anywhere else. I just want to open the file in my program and thats all...
0
 
membername86Author Commented:
> To Gwena - hm interesting, but how to get the images into the *.dat file ?
P.S. I`m a lamer...
0
 
GwenaCommented:
Hi :-)


 TackOnFile('MyBMP', 'myBMPs.dat', 'c:\Some.bmp');

This adds the bmp file named 'some.bmp' to the 'myBMPs.dat' file and gives the new image the name 'MyBMP'

You must be careful with this though because it does not check for an existing bmp in the dat file that already has the name 'MyBMP' and will add the new version while retaining the old... only the old one will be accessable.
I did not make deletion of existing data stored under  the same name automatic because sometimes a user will want to create a FIFO stack thingie with the data :-)


To ensure that any old version is removed before storing the new do this


 DelFromFile('MyBMP', 'myBMPs.dat');
 TackOnFile('MyBMP', 'myBMPs.dat', 'C:\Some.bmp');

The names that are used to access the bmp's are not case sensitive... so 'MYBMP' = 'mybmp' = 'MyBmP'

The best way to get used to how ExeMod works is with a demo... I can send you one if you like... something simple like 3 buttons

[select and add a bmp to dat file]
[retrieve and display a bmp from dat file]
[delete a bmp from dat file]

depending on what sort of app you are making it might be cool for you to store the bmp's inside the exe itself.. I can show you a demo for that also if you like... you can still add,alter,delete,retrieve the bmp's at runtime although the methods are a bit unusual :-)
0
 
membername86Author Commented:
> To Gwena => O.K. Let`s make a deal. Send me the ExeMod.pas file and the program and I`ll give You the points.
0
 
membername86Author Commented:
Ma mail - kamikaze@e-apollo.lv
0
 
Slick812Commented:
membername86, did you try my code? did it fail to work?
0
 
membername86Author Commented:
Not yet, cause Gwenas was the shortest. I`ll try to check Yourz tooo...
0
 
Slick812Commented:
thats because her's is in a "Component", if you don't need to have control of what you have in your file, or how you access the data there, then it's probally best to go for the shorter code
0
 
msa2003Commented:
membersname86: your decision.
0
 
GwenaCommented:
Hi :-)


   I just sent you a little demo... it's as simple as I could make it and still have the ability to work with bmp's in a data file... it has only a few lines of source code so it should be easy to follow

If you have any questions just email me or post here :-)

p.s. I don't care much about points so just leave the Question open here for a while... someone might come up with a better answer :-)

..Gwen..
0
 
GwenaCommented:
Oh... it might be better to save and retrieve jpg images instead of bmp's since they are so much smaller... it's just as easy to do as using bmp files.
0
 
msa2003Commented:
Gwena: if you use 30*30 images... hm... I think that JPEG's should be the same size or even larger ;)

P. S. Sometimes it is hard to determine what is really needed: simple to realize or simple to follow ;)
0
 
GwenaCommented:
Hi msa2003 :-)

  Yup... and there are so many ways to do something like this... so many choices :-)

If the images never need to change then res files is the easiest way.

If they need to change often then something like my way is good.

If you want to keep the user away from the images then putting them inside the exe scrambled up is the thing to do....simple XOR is usually enough to scramble so the user can't figure it out.
0
 
membername86Author Commented:
> To Gwena => No demo got :(. Maybe our mail server is down . It happens every 4`th day...
0
 
msa2003Commented:
Gwena:

I see there is no problem for EE users to download files from my site so I'd suggest you to send your demo to my e-mail: msa@dsip.net and I'll publish it as soon as it is possible.

membername86:

Sorry, I can't provide my own solution because a leak of time, but I'd like to help you anyway.
0
 
GwenaCommented:
OK msa2003 :-)

 I will send it to your email
 and I will re-send it to membername86's email

with luck we will get it to him :-)
0
 
Lukasz LachCommented:
show it... :-)
0
 
GwenaCommented:
Hi anAKiN :-)

Do you mean you want me to show the source for the demo?

well here it is... but it uses my exemod.pas unit and needs the latest version... that's a lot to post here so if you want that I can email it to you.. it's a few kb. Maybe msa2003 will post the demo on his site and you can dl the whole thing from there..  a compiled exe of the demo is in the zip along with all source, including latest exemod.pas... the exe is 90kb


here is it's source.. I hope it formats correctly... it's a qwik-n-dirty demo so it's very short and not bulletproof.


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs
  ,ExeMod, ExtCtrls, Buttons, StdCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    SpeedButton1: TSpeedButton;
    OpenDialog1: TOpenDialog;
    Edit1: TEdit;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    Edit2: TEdit;
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
Temp: String;
begin
OpenDialog1.Execute;
Temp := File2String(OpenDialog1.FileName);
SetCurrentDir(ExtractFilePath(Application.ExeName));
InsOrReplaceInFile(ExtractFileName(OpenDialog1.FileName),'MyBMPs.dat',Temp);
end;

procedure TForm1.SpeedButton2Click(Sender: TObject);
var
Temp: String;
BMP: TMemoryStream;
begin
If Edit1.Text <> '' then
begin
  If Edit1.Text = 'place name of bmp to display here' then exit;
  SetCurrentDir(ExtractFilePath(Application.ExeName));
  ExtractFromFile(Edit1.Text,'MyBMPs.dat',Temp);
  Bmp := TMemoryStream.Create;
  String2Stream(Temp,BMP);
  Image1.Picture.Graphic.LoadFromStream(BMP);
  BMP.Free;
end;
end;

procedure TForm1.SpeedButton3Click(Sender: TObject);
begin
If Edit2.Text = 'place name of BMP you wish to delete here' then exit;
SetCurrentDir(ExtractFilePath(Application.ExeName));
DelFromfile(Edit2.Text,'MyBMPs.dat');
end;

end.
 

0
 
msa2003Commented:
Comment from Gwena (don't accept it ;) ):

> Hi :-)


> I already put 3 tiny bmp's into the dat file..

> bbc.bmp
> ae.bmp
> bigones.bmp  (HeeHee)


> Just enter one of these names into the proper edit box
> and click [Display BMP] and it will appear

> You can enter the name of a bmp into the edit next to
> the [Delete BMP] button and remove it from the dat
> file

> Click the top button to add as many bmp's to the dat
> file as you like... they will store with their name as
> the storage name... but you could use any old name you
> like... bmp1.bmp...bmp2.bmp  or whatever...

> I can just as easily make the bmp's store into the exe
> itself as into an external file... if you want a demo
> of that just ask :-)


> ..Gwen..

I also placed demo here: http://www.serge.dsip.net/Gwena/demo.zip

I did't view or test it myself (sorry, I have no time) so the solution is provided as-is, withowt any warranty ;)

Good Luck!
0
 
msa2003Commented:
Typo, the right URL is http://www.serge.dsip.net/downloads/Gwena/demo.zip
Sorry ;)
0
 
membername86Author Commented:
Exemod is tha what I needed...
Thanx to Gwena & msa2003!!!
But who gets the points now?
0
 
membername86Author Commented:
>Gwena = I`ll send You my program when I will finish it... I`ll put Your name in it tooo..
0
 
msa2003Commented:
Gwen:
> p.s. I don't care much about points so just leave the Question open here for a while... someone might come up with a better answer :-)

;-)

I received enough points this month, so I don't care about'em too :o)
0
 
GwenaCommented:
I don't care anything about collecting points so give them to whoever... I just add comments to questions that interest me :-)  but if you must... go ahead and add them to my meager collection :-)

You don't have to add my name to your exe... your welcome to use my code any way you like.. I consider it all public domain.  But I would like to see what sort of program it is someday... so let me know where I can download it when you are done :-)


I just finished a cool lpt port controller app today... it will be posted on my website by tomorrow and uses exemod... so you can go look at that if you like..
It's called Portie :-)

By the way don't grab the copy of exemod from my site.. the version I sent you is newer and has a bugfix... I need to update it at Torry's soonest...


http://www.geocities.com/gacarpenter386/


0
 
msa2003Commented:
A have an interesting idea how to make the same that Gwen's unit using file streams in TImageList descendant. I didn't finished it yet, but I wont to do it. I don't wont to say that Gwen's unit is wrong (i even didn't look it), but I think that this is an another good idea.
0
 
CleanupPingCommented:
membername86:
This old question needs to be finalized -- accept an answer, split points, or get a refund.  For information on your options, please click here-> http:/help/closing.jsp#1 
EXPERTS:
Post your closing recommendations!  No comment means you don't care.
0
 
snehanshuCommented:
Hi!
No comment has been added lately and this question is therefore classified abandoned.

If asker wishes to close the question, then refer to
http://www.experts-exchange.com/help/closing.jsp

Otherwise, I will leave a recommendation in the Cleanup topic area that this question is:

Answered by: Gwena

Please leave any comments here within the next seven days. It is assumed that any participant not responding to this request is no longer interested in its final disposition.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

...Snehanshu
EE Cleanup Volunteer
0

Featured Post

Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

  • 11
  • 8
  • 8
  • +7
Tackle projects and never again get stuck behind a technical roadblock.
Join Now