How to have BufferedImages take up less RAM

So, the main thing I seem to be getting from this thread is that sprite sheets are a good way of reducing memory usage. The way my game is set up, the intermediate loading screen would not really work too well. Although, I’ll look into it anyway.

I’m going to use a profiler to see what things are taking the most resources. (I already know that BufferedImages take up around 99% of the RAM usage, so I’ll try and look more specifically at the methods.) I’ll update with any questions I have involving that later.

Other than that, I can’t seem to find many other ways to reduce the amount of RAM being taken up by the large quantity of images I have. If there are any other general tips anyone could provide when dealing with many resources, I would appreciate it.

There is no way around graphics taking up the amount of system RAM that they do. The best you can do is pack them all tightly into a single big BufferedImage and blit out chunks when you want.
“Blitting” btw is an old term, “blit” being short for “block image transfer”. In other words, copying a rectangle of pixels from one place to another. It’s almost always hardware accelerated these days, even with BufferedImage, and BufferedImage is a perfectly sensible way to do everything.

Of course if you wanted to do something a bit more modern you’d use JavaFX. And if you wanted to get a bit more fancy, LWJGL.

Cas :slight_smile:

My inclination is that a single BufferedImage might cause issues too? I’d meet in the middle, create a series of BufferedImage with multiple images on (sprite sheets), trying to group images that are used together on the same image. Maybe 1024x1024 (4MB) or 2048x2048 (16MB)? You want to give the images used in the same frame the best chance of being managed and cached in hardware together.

As @Abuse says, this is the opposite of the case. You want a VolatileImage if you are modifying the image a lot. If you don’t use a Graphics2D obtained from calling image.getGraphics() much or at all, you don’t need VolatileImage. Where people often fall down is they assume that managed images mean the Graphics2D from a BufferedImage is hardware accelerated - it isn’t!

I have some questions.

[quote]Java2D is creating power-of-2 textures that poorly fit the dimensions of your Image.
[/quote]
So, would it be better to have a 1024x1024 image with extra white or transparent pixels (whichever is more efficient) or have spritesheets of various sizes such as 960 x 905?

Also, would it be best to get the subImages when drawing each time (example two) or store them into BufferedImage objects and have them draw those without having to use the getSubImage() method any more times (example one)?

For example:


BufferedImage spriteSheet = ImageIO.read(fileLocation);

//Example one
BufferedImage exampleOne = spriteSheet.getSubimage(50, 50, 40, 40);
g.drawImage(exampleOne, 0, 0, null);

//Example two
g.drawImage(spriteSheet.getSubimage(50, 50, 40, 40), 0, 0, null);

Neither! Don’t create sub-images, use the variant of drawImage() that takes 8 arguments.

I see! That makes sense. Could you or someone else respond to the part in my previous post about the image dimensions and filler?

This is what I would do.
-> load tilesheet into a BufferedImage and convert it to int[]
-> create a BufferedImage that will handle the drawings with, again, an int[]
-> with this way, you will have to create your own drawing methods, since all image data is handled through int[]

I’m sceptical that Java2D is using power-of-two surfaces, but either way I’d use a uniform size as it’ll make things easier to deal with - 1024x1024 sounds like a good starting point. Definitely fill unused bits with transparent pixels if the image has transparency - there’s no difference in efficiency and no chance of getting any white pixels showing by accident.

What for?! For the purpose of ensuring there’s no way at all any capabilities of the GPU are used? :-\

Okay, you are right about that. But I don’t know the dimensions of his game. It would reduce the BufferedImages drastically and he would only need to get dta from the tilesheet int[] and copy it to the main int[] (the one that is being drawn)

A BufferedImage is basically a thin wrapper over an array, usually an int[]. Doesn’t really gain you anything, except the need to write a software renderer (which can be fun mind you, and faster than the software Java2D loops in some cases)

It wouldn’t probably affect the ram, but it would reduce the use of BI. Usually I use this method for small games in pure java which is very efficient. Now, he has 1000+ image objects, I don’t see how he can reduce the ram being used. Unless he creates a way to draw compressed images, which would be interesting

It seems that it has more to do with OpenGL textures based on the fourth bullet point of general comments here: https://community.oracle.com/docs/DOC-983015

I am using the JVM argument -Dsun.java2d.opengl=true. Does this mean that this affects me?

You’re brave! ;D That’s a very old document. There’s definitely code in the JDK source for GL to support non-power-of-two textures, it’s even mentioned in that document. I assume it’s working?! :-\

Never mind about the post I just deleted. The content was irrelevant.

It has been a while, but I had another question: How large should the maximum size of my spritesheets be if any limit should be imposed? (i.e. 2048 x 2048, 4096 x 4096, etc.)

These days 4096x4096 is generally supported in hardware but I notice that performance seems to drop off a cliff with Intel integrated GPUs at this size.

Cas :slight_smile:

Since you say that performance ‘drops off a cliff’ with Intel integrated GPUs at 4096 x 4096, would 2048 x 2048 be better?

Er, yes :slight_smile: Also my information is maybe a few years out of date - try it and see.

Cas :slight_smile:

Sounds good! I’ll see how it goes.

have you tried storing buffered images temporarily?
because a spritesheet is ONE image but it will be cropped into multiple buffered images right?

if thats the case try using something similar to this method to load a image.


public static Image getSprite(int id) {
    BufferedImage tempSprite; //isn't stored constantly?

    try {
        tempSprite = ImageIO.read(new File("spriteFolder/" + id + ".png);
        
    }
    catch(IOException e) {

    }

   return tempSprite;

}

i’ve used this way of loading images with the ‘drawImage’ method and it increased my performance alot. (still not sure if its the BEST way though)


drawImage(getSprite(2), 0, 0, 50, 50, null);