AffineTransform cuts away parts of my image

Hi!

I’ve written this little tool (http://www.itstud.chalmers.se/~larssont/ImageCreator.zip) to be able to generate rotated versions of an image. The way I’m currently doing the rotation isn’t working properly for some reason and I really can’t understand why so I’m hoping some of you guys could help me out.

To see what I mean just download and unzip the above file and run the JAR in it.

  1. File->Open…
  2. Select the image: images/ship_yellow.png
  3. Enter the number of frames to generate and click the “Generate” button.

It works with 4 frames but not with 8, the images gets cut of. The source is included in the zip and in the JAR as well. Please have a look at it.

Best Regards,

Johan Tibell

There are several problems with your image generator:

  1. Since the bounds of the image is modified by the rotation, when you call drawImage(source, 0, 0, null) for instance, you can no longer expect the top-left corner of the rotated source image to be at (0, 0) on the destination. Imagine it this way: A plain rectangle image stamped at (20, 0) and then rotated 45 degrees - you get an image whose minimum bounds are less than (20, 0).

Workaround:
Draw the current rotation onto an intermediate image first, then stamp that where you want it to be on the destination.

Example:


public BufferedImage generateRotatedImages(BufferedImage sourceImage, int numFrames)
      {
            // TODO Add support for arbitrary alpha values when Java gets fast enough.
            BufferedImage image = graphicsConfiguration.createCompatibleImage(sourceImage.getWidth()
                        * numFrames, sourceImage.getHeight(), Transparency.BITMASK);
            // An intermediate image to draw a rotated instance of the source image
            BufferedImage rotatedSource = graphicsConfiguration.createCompatibleImage(sourceImage.getWidth(),
                                                                                                                          sourceImage.getHeight(), sourceImage.getColorModel().getTransparency());
            
            Graphics2D g2d = image.createGraphics();
            g2d.setComposite(AlphaComposite.Src);

            // Generate the rotated images.
            int sourceWidth = sourceImage.getWidth();
            int sourceHeight = sourceImage.getHeight();
            double angleBetweenFrames = 2 * Math.PI / numFrames;
            int imageIndex = 0; // the current rotated image index
            for (double a = 0; a < 2 * Math.PI; a += angleBetweenFrames)
            {
                  Graphics2D rsG = rotatedSource.createGraphics();
                  rsG.rotate(a, rotatedSource.getWidth()/2, rotatedSource.getHeight()/2); // set rotation
                  rsG.drawImage(sourceImage, (rotatedSource.getWidth() - sourceWidth)/2, (rotatedSource.getHeight() - sourceHeight)/2, null);

                  g2d.drawImage(rotatedSource, imageIndex * sourceWidth, 0, null); // draw at specified coords

                  rsG.setComposite(AlphaComposite.Clear); // clear the image for use in the next iteration
                  rsG.fillRect(0, 0, rotatedSource.getWidth(), rotatedSource.getHeight());
                  rsG.dispose();
                  imageIndex++; // increment the image index
            }

            g2d.dispose();

            return image;
      }

  1. You would still get clipping when the square image is rotated and drawn onto a BufferedImage that is smaller than the size of the rotated image. Imagine a square image that is again rotated 45 degrees (to form a diamond shape) - the bounds of that rotated image is larger than the original, and when drawn onto a destination which is the same size as the square before rotation would result in clipping.

Workaround:
Add a bit of transparent buffer around your sprite image so that the sprite graphic you’re interested in doesn’t get clipped.

  1. You can also consider calling repaint() (as well as revalidate()) on your ImageCanvas whenever the frames are generated. Currently, for 1-4 frames of animation, the ImageCanvas is not updated to draw the generated image.

also take care of interpolation quality issues when rotating/transforming translucent images:

http://developer.java.sun.com/developer/bugParade/bugs/4950176.html

Cheers,

Mik

[quote]also take care of interpolation quality issues when rotating/transforming translucent images:

http://developer.java.sun.com/developer/bugParade/bugs/4950176.html

Cheers,

Mik
[/quote]
First of all, thanks a bunch both of you, I’ve almost solved my problems now. Regardsing the workaround you mentioned above I’m not quite sure how to “Using premultiplied image sources (such as INT_ARGB_PRE)”, could you provide an example on how to modify the above code to do that?

I have little or no experience with INT_ARG_PRE images. I wrote some test code and I remember it was slower than the INT_ARGB counterpart.
Additionally I had to call coercedata() in order to get something correctly displayed. Maybe I was wrong.

Jim Graham would be of great help for you.