ImageIO is an OK solution if you just want something quick and easy. For better efficiency, here is what I would recommend:
- Determine a way to only store what sprites are necessary in memory. This might involve extending Image with custom timers and an "expiration time." Or this may be "zone-based" (i.e. if the player exits the snow level, you can destroy the snow tiles..).
- Allocate some empty textures -- how many depends on how much memory you want to allot to texture data, and how many sprites will be needed in memory at once. (Sounds effects also tend to take up a lot of memory.)
- When the textures are needed (e.g. user enters snow level) then decode the sprite using PNGDecoder (or another of Matthias' decoders) and upload the byte buffer to one of your textures using glTexSubImage2D or a utility like this (note: if you use my PixelData utility, you'll want to add your own methods to deal more directly with the byte buffer).
- Ideally threading would be used to load data in the background, as to not hog the CPU
This technique has the downside of being rather complicated. Truth is, your game probably doesn't require a very complex system like this. Most games that require thousands of sprites do not require them all to be rendered at the same time, and so it would be better to design a simple loading/unloading system based on levels or zones. You can use Image() to load the image, and destroy() to unload the image. Or you can just stick to ImageIO, which seems to be working for your needs.