"hi res" textures

Riiight, so more questions by me. :stuck_out_tongue:

How do one go about handling hi res textures? Say the tile size of my game is 32x32, how can I apply a hi res texture to that? One that’s 128x128, do I just scale the texture to fit the 32x32 that the tile should be?

Right now I’m scaling my towers in my TD from 64x64 to 32x32, but is that the right way to do it?

Works for me.

Cas :slight_smile:

Well, in that way I guess it’ll work for me too.

Is this the right way to do it, in pure java?


AffineTransform tx = new AffineTransform();
tx.scale(0.5, 0.5);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
op.filter(this.actor.getImage(1), null);

I don’t actually rescale them permanently; I just draw them at whatever size I want them whenever I want them. (All my games operate on a system of having logical coordinates, mapped automatically to physical coordinates. Logically they are all 320x320 “pixels” in size)

Cas :slight_smile:

when you scale an image you get filtering issues when lowering the size more then half, because of the standard filter method.(see mipmaps for example).
So to get good results in a game one should either scale the images to the size they will be displayed or use mipmapping(should be quite easily implemented in java2d).

I read a some time ago a quite good blog article about scaleing images in java 2d and the final best method to do things was the following code.

public static BufferedImage getScaledImage(BufferedImage image, int targetWidth, int targetHeight) {
        int accWidth = image.getWidth();
        int accHeight = image.getHeight();

        BufferedImage result = image;
        do {
            accHeight = Math.max(accHeight / 2, targetHeight);
            accWidth = Math.max(accWidth / 2, targetWidth);
            
            BufferedImage tmp = new BufferedImage(accWidth, accHeight, image.getType());

            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                                        RenderingHints.VALUE_INTERPOLATION_BICUBIC);
            g2.drawImage(result, 0, 0, accWidth, accHeight, null);

            result = tmp;
        } while (accHeight != targetHeight && accWidth != targetWidth);

        return result;
    }

Try using SVG images by using the Batic library

This is almost identical to the example in Filthy Rich Clients - http://filthyrichclients.org/. It’s downloadable (with test code) from the chapter 4 examples. There are two major differences - it uses BILINEAR instead of BICUBIC, which is faster and probably more appropriate in this case; and they only allocate a single temporary image for all loops.

You could also switch to measuring the speed of your game in spf! :stuck_out_tongue:

yeah bilinear should be faster, but one should cache scaled images anyway, so speed doesn’t matter that much

I realise that. It’s actually the appropriateness I was more wondering about - most places I’ve seen this technique refer to it as progressive bilinear scaling. The halving of dimensions comes from the fact that bilinear samples from 2 pixels in each direction. Using an interpolation method that uses more pixels therefore seems somehow wrong - it may however be better.

yes because otherwise you would “miss” pixels(undersampling) while scaling. Some pixels wouldn’t contribute to the final image which can cause artifacts.
With bicubic filtering one has 4 samples in each direction, but the 2 outer samples contribute not that much to the output, because of which I still prefer to do the scaling steps only by half.

Hm, how can I improve my rendering? the fps falls when I have around 100 towers on the screen.

Here’s my tower render method:


AlphaComposite trans = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.7f);
AlphaComposite solid = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f);

@Override
public void render(Graphics g) {
	Graphics2D g2d = (Graphics2D) g;
		
	for(Entity e : this.shots) {
		e.render(g2d);
	}
		
	// Turret base shadow
	g2d.setComposite(this.trans);
	g2d.drawImage(this.actor.getImage(1), this.pos.x-16+1, this.pos.y-16+1, null);
		
	// Turret base
	g2d.setComposite(this.solid);
	g2d.drawImage(this.actor.getImage(0), this.pos.x-16, this.pos.y-16, null);
		
		
	AffineTransform tx = new AffineTransform();
	tx.rotate(this.rotation, 16, 16);
	AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
		
	// Turret shadow
	g2d.setComposite(this.trans);
	g2d.drawImage(op.filter(this.actor.getImage(5), null), this.pos.x-16+2, this.pos.y-16+2, null);
		
	// Turret
	g2d.setComposite(this.solid);
	g2d.drawImage(op.filter(this.actor.getImage(this.towerSprite), null), this.pos.x-16, this.pos.y-16, null);
}

At this point the sprites for the tower is already scaled (and cached in my spritesheet handling thing, for easy access if need be).

EDIT:
And for the curious, here’s my “good looking” tower: http://infloop.org/tmp/tower.png

EDIT EDIT:
Cleaned the rendering code a little, now it’s around 110 towers that the fps starts to drop.

@Regenuluz Try using the transform (rotate) on the Graphics2d itself. You’re not allowing the graphics pipeline to optimise by drawing in one command, as well as causing the op to continually create temporary images.

@Danny02 Yes, I know, though pretty sure cubic in Java2d is 3x3.

How would I use it directly on the g2d?

Use

g2d.rotate(this.rotation, x, y);

Bear in mind that the x and y will be different than you have now, at a guess this.pos.x and this.pos.y - needs to be in the coordinates of the Graphics2D, not your image.

If you’re using the Graphics2D across multiple methods you’ll also want to add


Graphics2D g2d = (Graphics2D) g;
AffineTransform originalTx = g2d.getTransform();
// rest of render method
g2d.setTransform(originalTx);

to remove the rotation afterwards.

You could also look at the version of drawImage() that takes a BufferedImageOp and pass your AffineTransformOp to that. This might allow it to be optimised too, though I’d stick with using the transform on the Graphics2D myself.

So for each tower you have about 4 draw calls? You should be able to get way higher then that.

I think that all the affine transformations is what is slowing you down. I am by no means a java2D pro but it seems like you have WAY too many composites and transforms going on. Simplest way for me to do rotation and alpha compositions is this.


Graphics2D g2d = (Graphics2D)g.create();
        g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) (fade)));
        g2d.setTransform(AffineTransform.getRotateInstance(rotation.x, loc.x, loc.y));
        //draw stuffs...


g2d.dispose();

Very fast. The x and y or in your screens coordinate space.