transparent png and Graphics.drawImage method

Hi,

I have a transparent PNG (640x320) file holding all tiles used in my game (each tile is 32x32 and has an id based on the position in the big PNG file.

I load this image into a Image object and then use the following method to draw individual tiles:

public abstract boolean drawImage(Image img, int dx1, int dy1,int dx2,int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer).

But the speed is very low, the frame rate makes it impossible to play. When I change to use the following drawImage variation:

public abstract boolean drawImage(Image img, int dx1, int dy1,int dx2,int dy2, int sx1, int sy1, int sx2, int sy2, Color color, ImageObserver observer).

The speed keeps very good (60 FPS). The problem is that the above drawImage will fill transparent portions of the image with color passed as an argument and I do not want that behaviour because I’m using another image as a background (parallax scrolling).

Do you guys know why there is this diference in speed of these two methods? And do you have a solution to keep the transparency?

Thanks a lot,
L. F. Estivalet

Load your image and then create individual BufferedImage for every sub image from that image. That way it will get accelerated. When you have a big image it isn’t accelerated I guess (but far from sure).

I think that creating multiple BufferedImages (one for each single tile) will consume too much memory instead of having just one big image holding all tiles, am I correct? I can have hundreds of tiles and I’m not sure about the performance also.

Let me know your thoughts about that.

have you tried? I don’t think it will matter, don’t know how much memory is used just for BufferedImage object (without data) but my guess is that it will be irrelevant. See how much your game uses memory now, do the conversion and compare it. Note that I don’t know much about it, just that big images aren’t accelerated :slight_smile:

So, if I understood you have a big alphablended tiling image. Each tile has pixels having 0-100% alpha transparency…

My guess: Without drawing a fill color might realize problems in non-hardware accelerated transparency (aka transluency). Fill color might produce some sidestep drawing algoritm thus does not suffer.

I think there are some “-Ddomagic=xxx” attribute to control transparency issues. But finding a good list of all available Java2D commandline arguments is a nightmare. I dont have a such list.

So do you guys use an array of BufferedImage to develop your tile based games?

And what about multiple layers (for parallax scrolling for example)? If you have say a game that is 640x480 and each tile is 32x32 than you will have the following structure:

BufferedImage[][] map = new BufferedImage[20][15]
BufferedImage[][] layer1 = new BufferedImage[20][15]
BufferedImage[][] layer2 = new BufferedImage[20][15]
BufferedImage[][] layer3 = new BufferedImage[20][15]

The example above we have a “map” that is where the main tiles are (where the player interacts) and layers 1, 2 and 3 are used just for background animations.

Is this the best or fast way of implementing parallax? I used to use large bitmaps and drawing only parts of it instead of having individual BufferedImages.

Umm, you mean like this one?
http://java.sun.com/javase/6/docs/technotes/guides/2d/flags.html

Chris

I’d really like to have a solution without using those “magic” attributes… So do you use the arrays like I mentioned in my previous post?

We’ve already had a thread cover this area.

The fastest painting method is :-

  1. To have your tiles stored in a single image to avoid excessive context switching in the graphics pipeline
  2. Use Graphics.drawImage(Image img, int dx1,int dy1,int dx2,int dy2,int sx1,int sy1,int sx2,int sy2,ImageObserver observer) to avoid the overhead of clipping.

One catch regarding this advice, is that under certain conditions there is a maximum texture size - beyond which HW acceleration will not occur.
This texture size limit is dependent upon several factors, including VM version, image type, and graphics drivers.

Regarding the alpha channel depth - it is almost always preferably to render a bitmasked image(1bit transparency), than one with a full alpha channel.(upto 8bit transparency).
The former will be accelerated on all 1.4+ Windows VMs, while the latter will require either a newer VM version, or several performance flags setting (to customize the rendering pipeline used).

Again, both aspects of this question have been covered many times before, if you want more thorough explanations, I suggest you try searching the forum.

no, the thing “we covered” isn’t a issue here… you are talking about live subimaging (as I remember), where you need to extract image from bigger one every loop. Here you can do this at load time so timing isn’t an issue. Now somebody answer this guy if it’s good to use arrays like he did or is there something better :slight_smile:

EDIT: I can think of simpler solution then original… you can still use your big tile palette image and split it into few smaller ones that have common tiles. Like all tiles for player. They will get accelerated, then you can use drawImage(coords of inside tile) on them. This way you have few BufferedImages instead each for every tile (as you were afraid it will consume too much memory).

note: images with translucency aren’t accelerated on windows, you should use translaccel=true property to accelerate them. You can just put “System.setProperty(“sun.java2d.translaccel”, “true”);” at beginning of your program and it will work.

Taking the possibility away to remove it on systems where it does not work, and after all whats about platforms where Java2D is hosted on backends without accalerated translucency? Things aren’t that easy, if you treat them like easy stuff you produce crap.

lg Clemens

wait a minute, I got impression that images with tranlucency aren’t accelerated only in windows due to directdraw not doing it, so everywhere else (linux, solaris, …?) it’s accelerated (they use opengl?). Where else besides windows it dosen’t work?
And if it dosen’t work then nothing will happen right?

The content of my original post does answer the original question.
It could have been clearer though, so i’ve restructured it so it better conveys its content.

To summarize, the poster had it right the first time - he was simply using unaccelerated 8bpp alpha translucency.

Changing to use bitmask transparency would have resolved the performance issues (either by altering the source image, or by coping the source image into a compatible BufferedImage with BITMASK transparency type)
Alternatively, they could enable the D3D or OGL pipelines to get accelerated full alpha rendering.

The former would give a more consistent speed increase across various VM versions, and platforms.

On Linux and other Unices X11 is mostly used as backend for Java2D - currently Java doesn’t support translucency on that backend. It will lead therefor to unplayable slow games. OpenGL is like ony windows only supported on a very limited range of driver/gpu combinations (mostly nvidia and worse ATI).
Bitmask transparency is definitivly the better choice here :slight_smile:

lg Clemens

I’m trying to use BITMASK stuff but it is not working


		Image tmp = GameRepository.get().getImage(pImageName);
		this.image = new BufferedImage(tmp.getWidth(null),tmp.getHeight(null),BufferedImage.BITMASK);
		this.image.getGraphics().drawImage(tmp,0,0,null);

It is still very slow! If I change to BufferedImage.TYPE_INT_RGB then it becomes faster but then I cannot see my background because the transparency of the tiles were removed!

What should I try next? ???

EDIT: Also tried the following


this.image = Util.CONFIGURATION.createCompatibleImage(tmp.getWidth(null),tmp.getHeight(null),BufferedImage.BITMASK);

It is very slow to play it. Not using transparency I get 60FPS and can play ok. But I really want to have tile transparency and my background.

The image “tmp” already contains transparency, right? When you draw it to this.image, the transparent pixels won’t be drawn because they’re transparent. this.image shouldn’t even look right unless there’s some more code to fix that.

I don’t think it will make any difference, but you should dispose the Graphics object as follows:

Graphics g = this.image.getGraphics();
g.drawImage(tmp, 0, 0, null);
g.dispose();

Not disposing Graphics objects ties up system resources.

You do have to use the GraphicsConfiguration.createCompatibleImage method.

as I remember they will be picked up by gc anyway, but it’s a good thing to call dispose whenever those methodes exist, that probably make it easier for gc or something