?
Solved

Save more than 1 bitmap in one file

Posted on 2003-03-03
39
Medium Priority
?
224 Views
Last Modified: 2013-11-19
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
Comment
Question by:membername86
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 11
  • 8
  • 8
  • +7
39 Comments
 

Expert Comment

by:lister07
ID: 8057340
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
 
LVL 5

Expert Comment

by:Lukasz Lach
ID: 8057723
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
 
LVL 3

Expert Comment

by:SteveWaite
ID: 8058363
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!

 
LVL 5

Expert Comment

by:msa2003
ID: 8059331
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
 
LVL 5

Expert Comment

by:msa2003
ID: 8059417
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
 
LVL 12

Expert Comment

by:esoftbg
ID: 8059872
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
 
LVL 5

Expert Comment

by:msa2003
ID: 8059975
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
 
LVL 34

Expert Comment

by:Slick812
ID: 8061740
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
 
LVL 5

Expert Comment

by:msa2003
ID: 8062428
You could also store images as an arrays of bytes using BlockWrite and read them using BlockRead.
0
 
LVL 34

Expert Comment

by:Slick812
ID: 8062520
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
 
LVL 5

Expert Comment

by:Gwena
ID: 8062902
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
 
LVL 1

Author Comment

by:membername86
ID: 8063473
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
 
LVL 1

Author Comment

by:membername86
ID: 8063503
> To Gwena - hm interesting, but how to get the images into the *.dat file ?
P.S. I`m a lamer...
0
 
LVL 5

Accepted Solution

by:
Gwena earned 700 total points
ID: 8065474
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
 
LVL 1

Author Comment

by:membername86
ID: 8079308
> 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
 
LVL 1

Author Comment

by:membername86
ID: 8079313
Ma mail - kamikaze@e-apollo.lv
0
 
LVL 34

Expert Comment

by:Slick812
ID: 8082023
membername86, did you try my code? did it fail to work?
0
 
LVL 1

Author Comment

by:membername86
ID: 8082162
Not yet, cause Gwenas was the shortest. I`ll try to check Yourz tooo...
0
 
LVL 34

Expert Comment

by:Slick812
ID: 8082315
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
 
LVL 5

Expert Comment

by:msa2003
ID: 8082521
membersname86: your decision.
0
 
LVL 5

Expert Comment

by:Gwena
ID: 8082783
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
 
LVL 5

Expert Comment

by:Gwena
ID: 8082810
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
 
LVL 5

Expert Comment

by:msa2003
ID: 8083783
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
 
LVL 5

Expert Comment

by:Gwena
ID: 8084239
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
 
LVL 1

Author Comment

by:membername86
ID: 8093045
> To Gwena => No demo got :(. Maybe our mail server is down . It happens every 4`th day...
0
 
LVL 5

Expert Comment

by:msa2003
ID: 8093783
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
 
LVL 5

Expert Comment

by:Gwena
ID: 8095562
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
 
LVL 5

Expert Comment

by:Lukasz Lach
ID: 8095614
show it... :-)
0
 
LVL 5

Expert Comment

by:Gwena
ID: 8096299
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
 
LVL 5

Expert Comment

by:msa2003
ID: 8097223
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
 
LVL 5

Expert Comment

by:msa2003
ID: 8097245
Typo, the right URL is http://www.serge.dsip.net/downloads/Gwena/demo.zip
Sorry ;)
0
 
LVL 1

Author Comment

by:membername86
ID: 8102499
Exemod is tha what I needed...
Thanx to Gwena & msa2003!!!
But who gets the points now?
0
 
LVL 1

Author Comment

by:membername86
ID: 8102511
>Gwena = I`ll send You my program when I will finish it... I`ll put Your name in it tooo..
0
 
LVL 5

Expert Comment

by:msa2003
ID: 8103669
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
 
LVL 5

Expert Comment

by:Gwena
ID: 8106606
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
 
LVL 5

Expert Comment

by:msa2003
ID: 8112879
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
 

Expert Comment

by:CleanupPing
ID: 9316964
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
 
LVL 5

Expert Comment

by:snehanshu
ID: 10033365
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

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Many programs have tried to outwit PowerPoint in terms of technology and skill. These programs, however, still lack several characteristics that PowerPoint has possessed from the start. Here's why PowerPoint replacements won't entirely work for desi…
In this Micro Tutorial viewers will learn the basic shortcuts and functions of Illustrator. The viewer will learn about the paintbrush tool, anchor points, font sizing, and more.
HTML5 has deprecated a few of the older ways of showing media as well as offering up a new way to create games and animations. Audio, video, and canvas are just a few of the adjustments made between XHTML and HTML5. As we learned in our last micr…
Suggested Courses
Course of the Month10 days, 14 hours left to enroll

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question