What might make Graphics2D.drawImage() slow?

I’m developing a physics sandbox game. I can easily fill the whole screen with rotated, scaled, textured boxes without noticing any FPS drops or excessive CPU usage, and drawing the character without a rotate transformation is fast.

However, if I rotate the character image, or scale it, CPU usage goes through the roof and there is a small but noticable FPS decrease. Using a profiler, I determined that drawImage() was the problem. As far as I can tell the character and texture images are being drawn the same way… It’s really confounding.

From the character:


private VolatileImage character = Util.getGraphicsConfiguration().createCompatibleVolatileImage(200, 200, Transparency.BITMASK); //I tried using Transparency.TRANSLUCENT
[...]
    public void draw(Graphics g) throws BuildismException
    {
        updateImage(); //This line draws the appropriate sections of the player's skin in the right locations. If I remove it, the character is a white box, but drawing it is still slow!
        Vec2D pos = getPart().getPosition().worldToScreen(Main.getView());
        int w = (int) (200*(Main.getView().scaleFactor/8));
        int h = (int) (200*(Main.getView().scaleFactor/8));
        int imgX = (int) pos.getX() - w/2;
        int imgY = (int) pos.getY() - h/2;
        AffineTransform orig = ((Graphics2D)g).getTransform();
        AffineTransform rotate = AffineTransform.getRotateInstance(-Math.toRadians(getPart().getRotation()), pos.getX(), pos.getY());
        ((Graphics2D)g).setTransform(rotate);
        g.drawImage(character, imgX, imgY, w, h, null); //I tried using a scale transform here, didn't help
        ((Graphics2D)g).setTransform(orig);
    }

For textured boxes (yes, I know this code is messy and unoptimized… but it works faster than the much simpler code for drawing a character!)


    public void setTexture(BufferedImage t)
    {
        if(t != null)
        {
            VolatileImage i = Util.getGraphicsConfiguration().createCompatibleVolatileImage(t.getWidth(), t.getHeight(), Transparency.TRANSLUCENT);
            Graphics2D g2 = (Graphics2D) i.getGraphics();
            Composite old = g2.getComposite();
            g2.setColor(transparent);
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT));
            g2.fillRect(0, 0, t.getWidth(), t.getHeight());
            g2.setComposite(old);
            g2.drawImage(t, 0, 0, null);
            texture = i;
        }
        else
            texture = null;
    }

[...]

            Graphics2D g2 = (Graphics2D) g;
            Vec2D worldPos = new Vec2D(body.getPosition());
            Vec2D imgPos = worldPos.add(new Vec2D(-getVec2DProp("Size").getX(), getVec2DProp("Size").getY()).mul(0.5)).worldToScreen(Main.getView());
            Vec2D rotateCenter = worldPos.worldToScreen(Main.getView());
            AffineTransform original = g2.getTransform();
            AffineTransform scale = AffineTransform.getScaleInstance(scaleDimensionForScreen(texture.getWidth(), getVec2DProp("Size").getX()), scaleDimensionForScreen(texture.getHeight(), getVec2DProp("Size").getY()));
            AffineTransform rotate = AffineTransform.getRotateInstance(-body.getAngle(), rotateCenter.getX(), rotateCenter.getY());
            g2.setTransform(rotate);
            g2.transform(scale);
            g2.drawImage(texture, (int) (imgPos.getX() / ((Main.getView().scaleFactor * getVec2DProp("Size").getX())/texture.getWidth())), (int) (imgPos.getY() / ((Main.getView().scaleFactor * getVec2DProp("Size").getY())/texture.getHeight())), null); //ew
            g2.setTransform(original);

Does anyone see a problem?

-do you use new() operator inside paintComponent(Graphics g), if so try to avoid this use preallocation wherever possible
-if you use composites this slows down too
-a VolatileImgage can get lost and should be checked for validity inside drawing, like here:
http://download.oracle.com/javase/6/docs/api/java/awt/image/VolatileImage.html
-instead of VolatileImage it is often recommended to use BufferStrategy:
http://download.oracle.com/javase/6/docs/api/java/awt/image/BufferStrategy.html
http://gpwiki.org/index.php/Java:Tutorials:Double_Buffering

Java scaling will be slow if you have the scaling on high quality. There is also a hardware scaling flag you can use.