Best Way to bundle pixmaps into 1 file

BlitzMax Forums/BlitzMax Beginners Area/Best Way to bundle pixmaps into 1 file

ima747(Posted 2008) [#1]
I'm looking for a good way to take a bunch of pixmaps and store them in 1 file, and extract them again.

Think there should be some way to convert them to and from TBanks and use loadbytearray and savebytearray to load and save them, but I can't quite get my head around how.

Additionally it would be nice if I could run them through the zlib compression functions as well to save space since a raw pixmap isn't going to have compression like a jpeg or png...

Maybe I'm going about this all wrong. Can anyone give any suggestions?


tonyg(Posted 2008) [#2]
Could you use Koriolis's zipstream module?


ima747(Posted 2008) [#3]
Interesting module, but I sadly no, I need to put other info in the file. Position information for the graphics etc.


Perturbatio(Posted 2008) [#4]
couldn't you store a text file with the same name as the image and keep the position info in there?


plash(Posted 2008) [#5]
Here's how I would do it: Have your image/pixmap bank (or 'map') recursively serialize and deserialize its own data; for example, I have a bunch of TTile objects that have TTileTexture objects attached (interfaced with a 'bank' or 'map', TTileMap), to serialize that I would write the tile information to the given stream, then pass the stream off to the texture, which would then write the pixmap and texture information. When deserializing we would read them in, in the exact same order. Using this method you can easily pass the serialize/deserialize stream as a bankstream, so you could create a bankstream (in memory), serialize, then encrypt the stream before writing it to the harddrive. Likewise for deserializing you could load the file back into a bankstream again and decrypt before passing it on to your deserialization methods.
I explain this in more detail here.


ima747(Posted 2008) [#6]
That's basicly what I'm trying to do Plash. However I can't seem to get it to load the images once I've stored them.

to strip it all down, here's where I'm currently at

Load pixmap
Convert pixmap to bank
write some data (an int through WriteInt() for example)
write bank to stream

All that works fine, if I remove the data and just write the bank I can open the file in an image editor and the picture is perfect, so that all seems to be working.

then on to loading

open a stream
read some data
read a bunch of data to the bank
convert bank to pixmap

reading the data is fine, and reading the bank looks fine but it can't convert it to a pixmap, so the data is clearly wrong. Using the above simplification if I don't write any data and just the pixmap, I can load it no problem, but if there's anything ahead of it it breaks.


re: Perturbatio. I need to enclose all the the elements of a given set in a single file for distribution so I'm trying to pack it all into 1 easily transmittable file. Along the conceptual lines of making a sprite animation picture with all your frames of animation. You only have to load 1 picture, and process the frames out of it rather than load hundreds of different frames of animation each from their own image. Once it's loaded it makes no difference, but keeping track of files, and sharing them between computers is a lot easier if there's only 1.


ImaginaryHuman(Posted 2008) [#7]
Are you using a pixmap window to map onto the bank memory when you've loaded?


plash(Posted 2008) [#8]
I use the PixmapDataType global (inside brl.pixmap) to write and read my pixmap objects. If you end up trying this (basically what you're already doing..?) you'll have to fix a bug in the module, see here.

I had the same issue, writing was fine, but then reading crashed everytime because PixmapDataType.ReadObject() was returning Null (returning nothing - curses Strict!).


tonyg(Posted 2008) [#9]
Could you save it all out to a single file using writestream with a 'header' for each pixmap then use createstaticpixmap to rebuild it?


tonyg(Posted 2008) [#10]
Could you save it all out to a single file using writestream with a 'header' for each pixmap and pixmappixelptr then use createstaticpixmap to rebuild it?


ima747(Posted 2008) [#11]
ImaginaryHuman: I was trying to load the data in the reverse of how it was written. Currently the pixmap is being put in the bank with SavePixmapPNG(pixmap, PixBank) and then writing that bank to the file with WriteBank(PixBank, Stream, StreamPos(Stream), BankSize(PixBank)) which writes it fine, as I can open the file as a png no problems.

to load it I create a bank of the appropriate size, then use ReadBank(PixBank, Stream, StreamPos(Stream), BankSize(PixBank)) which seems to work, in that it says it loads all the data. then pixmap = LoadPixmapPNG(PixBank) which should, I thought, load the pixmap out of the bank, but I get bubkis.


Plash: I moded pixmap.bmx with that missing return as you sugested and played around with PixmapDataType.ReadObject() a bit. It doesn't crash, and it gives me a pixmap, however it's just random junk. My assumption would be because I'm currently actually saving it as a PNG not a raw pixmap. But it is loading something and not crashing, which is further than I've been getting. So I should be able to save as a raw pixmap and load with the ReadObject(), which I may fall back to but if I can figure out a way to save as PNG then that will save a lot of space...

thanks for the help so far everyone, I may not have it working but I'm learning a lot... and having flash backs to when I first discovered pointers in C, with disasterous results... fyi, system memory is not a toy.


tonyg(Posted 2008) [#12]
Our posts crossed but you can write savepixmappng straight to an openstream without using the bank. I tried that but that is why you'd need a header giving the pixmapwidth/height etc if you want more than one file in the same output.


plash(Posted 2008) [#13]
You should be using PixmapDataType.WriteObject() too. IIRC you should be able to easily use compression on the bank buffer.


ima747(Posted 2008) [#14]
I think createstaticpixmap (I assume you mean off of the loaded bank) would, in theory save some memory if I was going to save the bank again elsewhere since I wouldn't have to convert it in and out of a pixmap object, but I think the end result would be the same and plash's readobject, and I don't know about the volatility of that memory. Since the static pixmap is just referencing a chunk of memory held in a bank for it's pixel data, if that bank is no longer being used (end of function the variable poofs) I don't know if Max knows to hold onto that memory because the pixmap is still pointing at it or if the pointer's reference just gets lost. Additionally I think the bank would have to be locked to keep it from moving and invalidating the pointer in the pixmap... but I'm not sure.


ima747(Posted 2008) [#15]
I need to clean up my code so it'll make sense out of the context of my game so I can post it here...

Try this, modified as needed with a picture etc.

Save Code
Stream = WriteStream("temp_file")
PixBank = CreateBank()
SavePixmapPNG(aLoadedPixmap, PixBank) ' corrected typo on this line
WriteInt(Stream, BankSize(PixBank))
WriteBank(PixBank, Stream, StreamPos(Stream), BankSize(PixBank))
CloseStream(Stream) ' close file

That should create a file with the pixmap saved in PNG, with a "header" that's an int (4 bytes) with the size of the saved PNG so you know how much to load, i.e. it's setup to put size, then PNG, then size then PNG for multiple files in 1 file.


Load Code
Stream = OpenStream("temp_file")
Size = ReadInt(Stream)
PixBank = CreateBank(Size)
ReadBank(PixBank, Stream, StreamPos(Stream), Size)
newPixMap = LoadPixmapPNG(PixBank)

That should, in theory, open the file, read the size of the PNG in the file (stored in the first 4 bytes as an int) then load the PNG data into a bank, then load the PNG bank into a usable pixmap. I think... but it doesn't...


plash(Posted 2008) [#16]
I don't see any problems with that code, except that I don't think SavePixmapPNG does not return a bank.

All you want is the compression of the PNG format, correct?
If so you could try just using my method and using the zlib compress functions on the stream.


ima747(Posted 2008) [#17]
yea, that was a typo on the savepixmappng line. I'm typing up a quick portable sample now rather that just an example. I think I may just be using readbank and writebank wrong... the documentation is pretty poor, namely the offset paramater I assumed was for the stream but now I think it's for the bank... but that should have been giving me overruns before...


ima747(Posted 2008) [#18]
yea, the offset shouldn't be streampos, it should be 0 because that's the bank offset. Yay for my stupidity + not explicit documentation. it's working now, here's a tryable example

PixmapToSave = LoadPixmap("test.png") ' load a picture


Print "Saving a Pixmap as a stackable PNG"
Stream = WriteStream("tempFile")
OutBank = CreateBank()
SavePixmapPNG(PixmapToSave, OutBank)
WriteInt(Stream, BankSize(OutBank))
WriteBank(OutBank, Stream, 0, BankSize(OutBank))
CloseStream(Stream) ' close file


Print "Loading a stacked PNG and converting to a pixmap"
Stream = ReadStream("tempFile")
Size = ReadInt(Stream)
InBank = CreateBank(Size)
ReadBank(InBank, Stream, 0, Size)
LoadedPixmap = LoadPixmapPNG(InBank) ' <- This is where it all goes wrong...
CloseStream(Stream)



' Display stuff to make sure it worked
ShowImage:TImage = LoadImage(LoadedPixmap, 0) ' turn the pixmap into a TImage so you can see alpha etc.

Graphics(1024,768,32,85,1)

Cls
SetBlend(ALPHABLEND)
While Not KeyHit(KEY_Escape)
	Cls
	DrawImage(ShowImage, 0, 0)
	Flip()
Wend



Thank you all for your help on this, it was very much appreciated.

To load multiple pictures you can just use ResizeBank after each images is fully loaded so you don't need to make a new bank every time if it's all in a loop. Or (like I will be doing in my game) you can hand the stream off to a loading function that loads each image, and other associated header/footer info before or after the png (position, rotation, scale, hit points whatever)

Additionally just use the other load/save pixmap functions for other graphics formats if png's arn't good for your game.