I’ve been homebrewing a slideshow application – more on that later – and learning lots about Java’s AWT graphics libraries. This is a post on a rookie error I made with image scaling.
TL;DR – The real performance hit is in creating image buffers and moving data between them. The actual calculations are almost trivial in comparison. So, creating one image buffer and then just drawing everything into it will be fastest.
OK, some code. First some common setup code:
BufferedImage img = ... // Get an image from somewhere BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics g = after.getGraphics(); // This just defines the transform, it doesn't apply it AffineTransform at = new AffineTransform(); at.setToTranslation(translateX, translateY); at.scale(1/ratio, 1/ratio); AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
This is slow:
scaleOp.filter(img, after);
Why? Because it creates a whole new BufferedImage, then copies it into after
.
This is about 100 times faster:
g.drawImage(img, (int) translateX, (int) translateY, (int) scaledWidth, (int) scaledHeight, Color.black, this);
Because the scary affine transform is gone, right? Wrong. This is fast too, about 50 times faster than the first version:
after = scaleOp.filter(img, null);
And this is just as fast as drawImage():
((Graphics2D) g).drawRenderedImage(img,at);
How would you choose between them? Well, if you want rotations you’d need to use the one of affine transform versions. Snippet 3 may involve you in color model shenanigans you’d rather avoid (see http://stackoverflow.com/questions/1841629/affinetransformop-speed-memory-question), so my conclusion is: if you don’t need rotations, use drawImage(), otherwise use drawRenderedImage(). However, I’m sure there are a thousand other ways to do this, and I’m nowhere near having a handle on best practice.