How to save images to a filestream?

Blitz3D Forums/Blitz3D Programming/How to save images to a filestream?

Doktor Zeus(Posted 2010) [#1]
One of the things I really hate when I'm coding is having to leave image files lying around in their natural state on the hard drive. I hate it! I mean, if I want to make a 'Congratulations!' screen for when the game is completed, it negates the point if someone can just search for it in the file menu and look at it using an image viewer. Also I like to keep all my game resourced together in neat packages, rather than having tons of small files lying around the place. What I really want is some means of saving an image to a filestream, but I'm a bit stumped how to do it. I guess I could manually output RGB from each pixel, but that sounds like it would be very slow to me, and anyway I'm not sure how you access the Alpha layer of textures. so here's what I could really use:

* I don't need compression or encryption, but it would be nice.
* The code has to be fast as I want the game to load 'on the fly' as much as possible.
* It must support alpha maps - very important!
* If it can write directly to and from Blitz Textures, that would be ideal, but again not 100% necessary as I can always just copyrect it into place.

Can anyone point me to some useful code or tutorial that might be able to help me solve this problem? It'd be greatly appreciated.

-Ash


Doktor Zeus(Posted 2010) [#2]
Forgot to mention, I need to be able to store multiple images in one file and extract only the data I need, so in other words I need to be able to extract data based on an index system. Don't want to have to open the entire file just to get at a single image.


Yasha(Posted 2010) [#3]
LockBuffer myBuffer
For x = ...
    For y = ...
        WriteInt myFileStream, ReadPixelFast(x, y, myBuffer)
    Next
Next
UnlockBuffer myBuffer


ReadPixelFast produces a standard four-byte integer containing RGB and alpha (if it exists) information. You can draw it back to the image with WritePixelFast. Note though that an image with an alpha channel loaded into a 2D image will lose the alpha information (it's just replaced with 255). Not sure what happens if you load a texture with alpha channel into a texture that wasn't asked to support it, but I wouldn't count on it preserving it there either.

Slightly faster would be to copy the pixels to a bank, and write them all at once with WriteBytes. This would also let you measure the size of the image (although calculating it is trivially easy) to store data pointers to each image's name and data block at the start of the file first.

Anyway outputting the RGBA of each pixel is actually the fastest way of doing it that keeps it entirely within B3D; compression is certainly possible but likely to be very slow; or you could use a DLL to dump the (possibly compressed) image data in a bank, and save it with WriteBytes, but I for one don't know what DLLs are available for this.


Doktor Zeus(Posted 2010) [#4]
Thanks a bunch! I've not worked with banks before, but I'll give that a go in the morning. I didn't think something that simple would work (and didn't know ReadPixelFast would also grab the alpha channel). With that data I should be able to handle the indexing feature myself.

Again thanks. I've no idea why I couldn't find this solution somewhere in the codebase. Maybe people think it's just too simple. Guess I'm just too used to languages that do all the hard work for you!

If anyone else has tips, I'd be happy to hear them too


Doktor Zeus(Posted 2010) [#5]
Right, I've been trying this method (writing image data to a bank, then saving that bank to a filestream). It works, but as I feared it seems very, very slow compared to using commands such as LoadTexture. On an 800 x 600 image it seems to take a little over a second to load. Using LoadTexture I didn't even notice a dip in the framerate, and that was from a BMP file, not raw data like this. Can anyone suggest a different tactic, possibly using DLLs or something similar, or point out what it is I'm doing wrong?

Function WriteImage(Stream, Image)
	W = TextureWidth(Image) : H = TextureHeight(Image)
	Z = W * H * 4 + 4
	DataHolder = CreateBank(Z)
	PokeShort DataHolder, 0, W : PokeShort Dataholder, 2, H

	LockBuffer TextureBuffer(Image)
	For X = 0 To W - 1
		For Y = 0 To H - 1
			PokeInt DataHolder, (Y*W+X+1)*4, ReadPixelFast(X, Y, TextureBuffer(Image))
		Next
	Next
	UnlockBuffer TextureBuffer(Image)
	
	WriteInt(Stream,Z) : WriteBytes(DataHolder, Stream, 0, Z)	
	FreeBank DataHolder
End Function


Function ReadImage(Stream, Entity)
	Z = ReadInt(Stream)
	DataHolder = CreateBank(Z)
	ReadBytes DataHolder,Stream,0,Z
	
	W = PeekShort(DataHolder,0) : H = PeekShort(DataHolder,2)
	Image = CreateTexture(W,H)
		
	LockBuffer TextureBuffer(Image)
	For X = 0 To W - 1
		For Y = 0 To H - 1
			C = PeekInt(DataHolder, (Y*W+X+1)*4)
			WritePixelFast(X, Y, C, TextureBuffer(Image))
		Next
	Next
	UnlockBuffer TextureBuffer(Image)
	FreeBank DataHolder	
	
	EntityTexture Entity,Image
End Function