Faster Imaging Scaling

I’m using Raycasting in a game I am creating, and image mapping surfaces requires a lot of image rescaling. Currently I’m using the Graphics2D.draw(BufferedImage, AffineTransform, ImageObserver) method to draw the images. The AffineTransform contains both translation and scaling information.
I’m using a profiler (Optimizeit) to determine any bottlenecks, and it appears that 60% of all CPU time in the game is spent executing that draw() method, and digging into it deeper, 40% is spent just rescaling the image. I’ve tried writing my own scaling algorithm using PixelGrabber, but that gave way worse results. Is there anything that I can do to speed up image scaling?
Thank you!

Do you grab pixels each time the rescaling is done? If so, this is not good, as PixelGrabber is a very slow class.

It would be better to keep the images in a 1D int array, and do the scaling completely in integer arrays. Then, when you are done with the scaling, you can get an image out of the array using java.awt.image.MemoryImageSource.

Still I would think that this takes up significant time. If possible, I would consider caching the scaled images.

Is there a reason your using AWT? Because MemoryImageSrc and PixelGrabbing are horrible. BufferedImages are the way to go. Especially if your using java2d.

Wether your using BufferedImages or MemImgsrc, scaling is relatively simple to implement in software, especially for rectangular areas as in a raycaster. And it will be alot faster than AffineTransform. For every pixel in your scaled image interpolate to get the pixel from the source image; google should yield a plethora of information.

Thanks for your responses. I currently am not using PixelGrabber. I am using AffineTransform. I only mentioned PixelGrabber as an alternate test that I performed that gave much worse results. I went ahead and tried using MemoryImageSource in my custom scaling, but it is still slower than the AffineTransform.

nonnus29, when you mentioned to just use a BufferedImage, how are you suggesting it be populated? I tested it out as well, by simply calling the setRGB() method with the int array, but it gave about the same performace as the MemoryImageSource did.

Of all of the tests that I’ve done, AffineTransform seems to be the fastest, but only by comparison, because it is pretty slow as well.

No problem; in fact this should go in a faq somewhere;


DataBufferInt data = (DataBufferInt)image.getRaster().getDataBuffer() ;
int [] buffer = data.getData() ;

Any changes you make to the int[] will effect the image and you must be certain that image is of type BufferedImage.TYPE_INT_ARGB.

Thanks again for the clarification, nonnus29.

I went ahead and tried it using the DataBufferInt, but is still slower. Here’s the custom scale method I’m using. It’s custom, because I’m only scaling the height, and the images will keep their fixed-width of 1 pixel:

    private Image customScale(int[] theImage, int destHeight) {
        int srcHeight = theImage.length;

        BufferedImage bi = new BufferedImage(1, destHeight, BufferedImage.TYPE_INT_ARGB);

        DataBufferInt data = (DataBufferInt)bi.getRaster().getDataBuffer();
        int[] destPix = data.getData();

        float srcY = 0;
        float dy = (float)srcHeight/destHeight;

        for (int i = 0; i < destHeight; i++) {
            destPix[i] = theImage[(int)srcY];
            srcY += dy;
        }

        return bi;
    }

And then I’m just calling the Graphics2D.drawImage() method.

When I use the AffineTransform like so:

affineTransform.setToTranslation(x, y);
wallAT.scale(1, wallHeight/imageHeight);

And then call the Graphics2D.drawImage() method that takes an AffineTransform I get twice the frames per second, but still isn’t what I would like.

Can anyone test these two approaches for actual comparison purposes?

I don’t think I’m following you.

I assume customScale() is called for every wall slice; its most likely a bad idea to be creating a new instance of BufferedImage bi multiple times per frame.

Why not use a BufferedImage for the backbuffer, a BufferedImage for the wall textures and simply perform pixel operations between the two? I’d have something like this:


public class Renderer {
  BufferedImage bbuffer, walltexture;
  ...
  public void render()  {
    ...
    drawslice();
  }
  drawslice()  {...}
}

So you don’t have to pass any parameters around. Thats just my $.02.

EDIT: plus you only have to call drawimage() once when you draw the backbuffer on the Graphics.

Thanks, nonnus29.
I tried your suggestion, and it is definately faster than the customScale method I had before. It performs just about the same, however, as the AffineTransform approach. But at least I have a little more control at the pixel level which will allow me to potentially implement some neat effects later.

Thanks again. I’m still on the lookup for the killer scale routine…perhaps something hardware accelerated. :slight_smile: