Board index » cppbuilder » Re: Loading multiple images from a single file.

Re: Loading multiple images from a single file.


2003-07-23 02:24:56 AM
cppbuilder42
does TimageList do what you need ?
Vimm wrote:
Quote
I'm using BCB 5 and I'm looking for a way to load graphics from a file.
I know how to load "abc.jpg" from a jpg file, etc, but my goal is to
have multiple images in a single file which the main program can access.
I was looking at the files for a game called "Magic Online" and it's a
perfect example of what I want to do. The user downloads a library
containing all the card images and the program plucks out an image when
it's required. To update the images, the user simply downloads the new
library file.

Is there any way I could create an image library consisting of a single
file? My program currently consists of a seperate program for each
image library but I'd like to make it into one program which simply
loads the images when they're needed. Thanks for any help anyone can
provide.

Vimm
 
 

Re:Re: Loading multiple images from a single file.

Thanks a lot, that's a big help. I've gotten the .dat file created,
however I haven't been able to figure out how to read the header data
from the file. I can't figure out how to read the data from the header
as an int so I can set the position.
Vimm
Damon Chandler (TeamB) wrote:
Quote
Hi Vimm,

>Is there any way I could create an image library consisting
>of a single file?


To create your image library, create a "source" TFileStream object with
read acess (fmOpenRead) to the original image file, and create a "target"
TFileStream object with write access (fmCreate) to the library file. Then,
you'd load your image from the source stream using the
TBitmap::LoadFromStream() method (or the same method of TJPEGImage), and
then use the TStream::CopyFrom() method of the target stream to copy the
bitmap (or JPEG) data from the source stream to the target stream. You'd
then repeat this process using a different source stream (i.e., the next
image) and the same target stream. The idea is to write multiple source
streams to a single target stream. The target stream (i.e., your library
file) would end up looking like...

<header>
<data for Image1>
<data for Image2>
<data for Image3>
...
<data for ImageN>

In the header--which you should write to the target stream before calling
CopyFrom() to store the images--you'd write the offset in bytes from the
beginning of the stream to each image. Then, to read an image from your
library file, you'd create a TFileStream with read access to the file.
Next, you'd read the header to determine how many bytes to move to access
the desired image, and then you'd seek to that location by setting the
TStream::Position property. Finally, you'd pass your (properly offset)
TFileStream object to the TBitmap::LoadFromStream() (or the same method of
TJPEGImage) to decode the image data. This technique works because the
LoadFromStream() method starts reading from the stream's current position
(which you've offset based on the desired image).

The following post provides code for a similar technique; it should get you
started...

tinyurl.com/hnh2

Let me know if you run into trouble.

Good luck,
 

Re:Re: Loading multiple images from a single file.

Ah forget it, I'll figure it out. Right now I've got it working for two
images but it doesn't seem to want to load the 3rd one. I'm self taught
on this software and most of what I know I learned from reading these
newsgroups, so if I haven't done something before I have no clue how to
do it. Thanks for the help.
Vimm
Vimm wrote:
Quote
Thanks a lot, that's a big help. I've gotten the .dat file created,
however I haven't been able to figure out how to read the header data
from the file. I can't figure out how to read the data from the header
as an int so I can set the position.

Vimm



Damon Chandler (TeamB) wrote:

>Hi Vimm,
>
>>Is there any way I could create an image library consisting of a
>>single file?
>
>
>
>To create your image library, create a "source" TFileStream object with
>read acess (fmOpenRead) to the original image file, and create a "target"
>TFileStream object with write access (fmCreate) to the library file.
>Then,
>you'd load your image from the source stream using the
>TBitmap::LoadFromStream() method (or the same method of TJPEGImage), and
>then use the TStream::CopyFrom() method of the target stream to copy the
>bitmap (or JPEG) data from the source stream to the target stream. You'd
>then repeat this process using a different source stream (i.e., the next
>image) and the same target stream. The idea is to write multiple source
>streams to a single target stream. The target stream (i.e., your library
>file) would end up looking like...
>
><header>
><data for Image1>
><data for Image2>
><data for Image3>
>...
><data for ImageN>
>
>In the header--which you should write to the target stream before calling
>CopyFrom() to store the images--you'd write the offset in bytes from the
>beginning of the stream to each image. Then, to read an image from your
>library file, you'd create a TFileStream with read access to the file.
>Next, you'd read the header to determine how many bytes to move to access
>the desired image, and then you'd seek to that location by setting the
>TStream::Position property. Finally, you'd pass your (properly offset)
>TFileStream object to the TBitmap::LoadFromStream() (or the same
>method of
>TJPEGImage) to decode the image data. This technique works because the
>LoadFromStream() method starts reading from the stream's current position
>(which you've offset based on the desired image).
>
>The following post provides code for a similar technique; it should
>get you
>started...
>
>tinyurl.com/hnh2
>
>Let me know if you run into trouble.
>
>Good luck,


 

{smallsort}

Re:Re: Loading multiple images from a single file.

Vimm,
Quote
Right now I've got it working for two images but it doesn't
seem to want to load the 3rd one.
Here's an example that demonstrates how to do what I was talking about.
First, here's a function that will create the library file given a target
filename and a list of source image files...
#include <memory>
#include <vector>
void WriteImageLibrary(
AnsiString dst_fname,
TStringList& src_fnames
)
{
// get the number of file names (images)
UINT const num_images = src_fnames.Count;
// create a destination (output) file stream
std::auto_ptr<TFileStream>dst_stream(
new TFileStream(dst_fname, fmCreate)
);
// write the number of images
dst_stream->Write(&num_images, sizeof(num_images));
// make room for the image positions
dst_stream->Seek(num_images*sizeof(UINT), soFromCurrent);
// write the image data...
std::vector<UINT>src_sizes;
for (UINT index = 0; index < num_images; ++index)
{
// open the source stream
std::auto_ptr<TFileStream>src_stream(
new TFileStream(src_fnames.Strings[index], fmOpenRead)
);
// copy the source stream to the destination stream
// and store the source stream size in the vector
src_sizes.push_back(
dst_stream->CopyFrom(src_stream.get(), 0)
);
}
// rewind the stream just past the 'num_images' field
dst_stream->Seek(sizeof(num_images), soFromBeginning);
// write the image positions (number of bytes from the
// beginning of the stream to the start of each image)...
UINT src_pos = sizeof(num_images) + num_images*sizeof(UINT);
dst_stream->Write(&src_pos, sizeof(src_pos));
for (UINT index = 0; index < num_images - 1; ++index)
{
src_pos += src_sizes[index];
dst_stream->Write(&src_pos, sizeof(src_pos));
}
}
The header of the library file that this code generates consists of (in
order)...
1. A UINT that specifies the total number of images.
2. A UINT that specifies the number of bytes from the
beginning of the stream to the 1st image chunk.
3. A UINT that specifies the number of bytes from the
beginning of the stream to the 2nd image chunk.
...
N. A UINT that specifies the number of bytes from the
beginning of the stream to the Nth image chunk.
So, the size of the header will always be sizeof(UINT) +
num_images*sizeof(UINT). The image streams come after the header data.
Here's an example of using the WriteImageLibrary() function...
void __fastcall TForm1::CreateLibButtonClick(TObject *Sender)
{
// define the source images...
std::auto_ptr<TStringList>src_fnames(new TStringList());
src_fnames->Add("c:/damon/images/bitmap1.bmp");
src_fnames->Add("c:/damon/images/bitmap2.bmp");
src_fnames->Add("c:/damon/images/bitmap3.bmp");
// create the library file
WriteImageLibrary("c:/dc.bmps", *src_fnames.get());
}
Here's a function that does the opposite; it reads a specific image from
the library file...
Graphics::TBitmap* ReadImageLibrary(
AnsiString fname,
UINT img_index
)
{
// open the library file
std::auto_ptr<TFileStream>stream(
new TFileStream(fname, fmOpenRead)
);
// read the number of images
UINT num_images;
stream->Read(&num_images, sizeof(num_images));
// check that the index is within range
if (img_index>= num_images)
{
throw Exception("ReadImageLibrary()--Invalid index.");
}
// read the position of the specified image stream
UINT img_pos;
stream->Seek(img_index*sizeof(UINT), soFromCurrent);
stream->ReadBuffer(&img_pos, sizeof(UINT));
stream->Seek(img_pos, soFromBeginning);
// read the actual image data
std::auto_ptr<Graphics::TBitmap>
Bitmap(new Graphics::TBitmap());
Bitmap->LoadFromStream(stream.get());
// return a pointer to the bitmap
// (caller assumes ownership)
return Bitmap.release();
}
This function returns a pointer to the TBitmap object that contains of the
specified image (given by its zero-based index in the library file); here's
how you'd use it...
void __fastcall TForm1::ReadImgButtonClick(TObject *Sender)
{
int const some_index = // choose an image index
std::auto_ptr<Graphics::TBitmap>Bitmap(
ReadImageLibrary("c:/dc.bmps", some_index)
);
Image1->Picture->Bitmap->Assign(Bitmap.get());
}
HTH,
--
Damon (TeamB)
BCBCAQ - bcbcaq.bytamin-c.com
 

Re:Re: Loading multiple images from a single file.

Thanks a ton for the help. I tweaked your code a bit to make it work
for jpegs and now my program's working like I've always wanted it to.
Thanks again.
Vimm
Vimm wrote:
Quote
Wow, thanks a lot. That's more than I ever hoped for. I did manage to
finally get it working by treating the header like a standard text file
and using IntToStr() and StrToInt() to read and write the values. I'm
sure my code was horribly inefficient, but at least it worked!
Unfortunately when I went to borrow some code from a backup copy it
overwrote my working copy and I lost it all.

Thanks a lot, this'll be much faster than starting from scratch again.
I've been wanting to have this functionality in my program for a long
time but never found a way to do it.

Vimm



Damon Chandler (TeamB) wrote:

>Vimm,
>
>>Right now I've got it working for two images but it doesn't seem to
>>want to load the 3rd one.
>
>
>
>Here's an example that demonstrates how to do what I was talking
>about. First, here's a function that will create the library file
>given a target
>filename and a list of source image files...
>
>#include <memory>
>#include <vector>
>void WriteImageLibrary(
>AnsiString dst_fname,
>TStringList& src_fnames
>)
>{
>// get the number of file names (images)
>UINT const num_images = src_fnames.Count;
>// create a destination (output) file stream
>std::auto_ptr<TFileStream>dst_stream(
>new TFileStream(dst_fname, fmCreate)
>);
>
>// write the number of images
>dst_stream->Write(&num_images, sizeof(num_images));
>// make room for the image positions
>dst_stream->Seek(num_images*sizeof(UINT), soFromCurrent);
>
>// write the image data...
>std::vector<UINT>src_sizes;
>for (UINT index = 0; index < num_images; ++index)
>{
>// open the source stream
>std::auto_ptr<TFileStream>src_stream(
>new TFileStream(src_fnames.Strings[index], fmOpenRead)
>);
>// copy the source stream to the destination stream
>// and store the source stream size in the vector
>src_sizes.push_back(
>dst_stream->CopyFrom(src_stream.get(), 0)
>);
>}
>
>// rewind the stream just past the 'num_images' field
>dst_stream->Seek(sizeof(num_images), soFromBeginning);
>
>// write the image positions (number of bytes from the
>// beginning of the stream to the start of each image)...
>UINT src_pos = sizeof(num_images) + num_images*sizeof(UINT);
>dst_stream->Write(&src_pos, sizeof(src_pos));
>for (UINT index = 0; index < num_images - 1; ++index)
>{
>src_pos += src_sizes[index];
>dst_stream->Write(&src_pos, sizeof(src_pos));
>}
>}
>
>The header of the library file that this code generates consists of (in
>order)...
>
>1. A UINT that specifies the total number of images.
>2. A UINT that specifies the number of bytes from the beginning
>of the stream to the 1st image chunk.
>3. A UINT that specifies the number of bytes from the beginning
>of the stream to the 2nd image chunk.
>...
>N. A UINT that specifies the number of bytes from the beginning
>of the stream to the Nth image chunk.
>
>So, the size of the header will always be sizeof(UINT) +
>num_images*sizeof(UINT). The image streams come after the header data.
>
>Here's an example of using the WriteImageLibrary() function...
>
>void __fastcall TForm1::CreateLibButtonClick(TObject *Sender)
>{
>// define the source images...
>std::auto_ptr<TStringList>src_fnames(new TStringList());
>src_fnames->Add("c:/damon/images/bitmap1.bmp");
>src_fnames->Add("c:/damon/images/bitmap2.bmp");
>src_fnames->Add("c:/damon/images/bitmap3.bmp");
>
>// create the library file
>WriteImageLibrary("c:/dc.bmps", *src_fnames.get());
>}
>
>Here's a function that does the opposite; it reads a specific image from
>the library file...
>
>Graphics::TBitmap* ReadImageLibrary(
>AnsiString fname,
>UINT img_index
>)
>{
>// open the library file
>std::auto_ptr<TFileStream>stream(
>new TFileStream(fname, fmOpenRead)
>);
>
>// read the number of images
>UINT num_images;
>stream->Read(&num_images, sizeof(num_images));
>
>// check that the index is within range
>if (img_index>= num_images)
>{
>throw Exception("ReadImageLibrary()--Invalid index.");
>}
>
>// read the position of the specified image stream
>UINT img_pos;
>stream->Seek(img_index*sizeof(UINT), soFromCurrent);
>stream->ReadBuffer(&img_pos, sizeof(UINT));
>stream->Seek(img_pos, soFromBeginning);
>
>// read the actual image data
>std::auto_ptr<Graphics::TBitmap>
>Bitmap(new Graphics::TBitmap());
>Bitmap->LoadFromStream(stream.get());
>
>// return a pointer to the bitmap
>// (caller assumes ownership)
>return Bitmap.release();
>}
>
>This function returns a pointer to the TBitmap object that contains of
>the
>specified image (given by its zero-based index in the library file);
>here's
>how you'd use it...
>
>void __fastcall TForm1::ReadImgButtonClick(TObject *Sender)
>{
>int const some_index = // choose an image index
>std::auto_ptr<Graphics::TBitmap>Bitmap(
>ReadImageLibrary("c:/dc.bmps", some_index)
>);
>Image1->Picture->Bitmap->Assign(Bitmap.get());
>}
>
>HTH,


 

Re:Re: Loading multiple images from a single file.

On Thu, 24 Jul 2003 19:50:29 -0400, Vimm < XXXX@XXXXX.COM >wrote:
Quote
Thanks a ton for the help.
[snipped 3 lines of reply and 150+ lines of unnecessary quotes]
Vimm -
Please trim quotations as per the posting Guidelines
and "Etiquette" at info.borland.com/newsgroups/
Only quote enough to show the context of your reply.
There is rarely a need to quote an entire message
and never a need to quote a complete thread. Thanks.
 

Re:Re: Loading multiple images from a single file.

Maybe you got the answer, but still...
Have u looked at,
Canvas->CopyRect(...)
since in my application, i have also did the same thing, have one single BMP
for all the image, then wth the help of copyrect, at runtime i got the
rectangle region i required
this is much more efficient, than the above one,
-Dumboo :-)
"Vimm" < XXXX@XXXXX.COM >wrote in message
Quote
I'm using BCB 5 and I'm looking for a way to load graphics from a file.
I know how to load "abc.jpg" from a jpg file, etc, but my goal is to
have multiple images in a single file which the main program can access.
I was looking at the files for a game called "Magic Online" and it's a
perfect example of what I want to do. The user downloads a library
containing all the card images and the program plucks out an image when
it's required. To update the images, the user simply downloads the new
library file.

Is there any way I could create an image library consisting of a single
file? My program currently consists of a seperate program for each
image library but I'd like to make it into one program which simply
loads the images when they're needed. Thanks for any help anyone can
provide.

Vimm