2

I have document images of varying dimensions and I want to be able to efficiently scale and rotate them in the following manner (standard "Rotate" and "Zoom" logic). How do I do it?

An image is H pixels high and W pixels wide. Initially, it should scale to 600 pixels wide. On each rotation, the panel's width and height should swap and the scaled image should rotate 90 degrees. On each zoom, the image should scale by factor "scale".

Here's what I've tried so far on BufferedImage img... the resulting BufferedImage scales and rotates but does not translate (to be centered atop the panel after a 90-degree rotation):

double scale = zoom * 600.0 / img.getWidth();
rotation = (rotation + degrees) % 360;
int scaledWidth = (int)(scale * img.getWidth());
int scaledHeight = (int)(scale * img.getHeight());
BufferedImage bufferedImage = new BufferedImage(scaledWidth, scaledHeight, img.getType());
if (rotation % 180 == 0)
    bufferedImage = new BufferedImage(scaledWidth, scaledHeight, img.getType());
else
    bufferedImage = new BufferedImage(scaledHeight, scaledWidth, img.getType());

AffineTransform transform = AffineTransform.getRotateInstance(Math.toRadians(rotation), scaledWidth/2, scaledHeight/2);
transform.scale(scale, scale);
AffineTransformOp operation = new AffineTransformOp(transform, AffineTransformOp.TYPE_BILINEAR);
scaledImage = operation.filter(img, bufferedImage);
imagePanel.setPreferredSize(new Dimension(bufferedImage.getWidth(), bufferedImage.getHeight()));
Daniel
  • 855
  • 10
  • 21
  • 1
    You could take a look at [this example](http://stackoverflow.com/questions/11959758/java-maintaining-aspect-ratio-of-jpanel-background-image/11959928#11959928) for scaling while maintaining the aspect ratio of an image and [this example](http://stackoverflow.com/questions/15779877/rotate-bufferedimage-inside-jpanel/15780090#15780090) of a free rotating image and [this example](http://stackoverflow.com/questions/11911610/affinetransform-rotate-how-do-i-xlate-rotate-and-scale-at-the-same-time/11911758#11911758) for a fixed 90 degree rotation... – MadProgrammer Nov 08 '13 at 00:51
  • Thanks MadProgrammer, I think I need to freely rotate the image and then translate it by some distance. – Daniel Nov 08 '13 at 00:57
  • Just remember, rotation is fun...especially at 2 in the morning ;) – MadProgrammer Nov 08 '13 at 01:06
  • I've updated my solution to include documented code for automagically scaling, rotating, and translating images. Can I improve it further? – Daniel Nov 08 '13 at 16:26

1 Answers1

2

Aha! The key (the JavaDoc was confusing) was realizing that on AffineTransform, rotate() and other methods transform the matrix, not the image! The following code works automagically!

/**
 * Transforms the image efficiently without losing image quality.
 * Scales the image to a width of (600 * scale) pixels, rotates the image,
 * and translates (moves) the image to recenter it if rotated 90 or 270 degrees.
 */
protected BufferedImage transformImage(BufferedImage image)
{
    int scaledWidth = (int)(scale * image.getWidth());
    int scaledHeight = (int)(scale * image.getHeight());

    // Methods AffineTransform.rotate(), AffineTransform.scale() and AffineTransform.translate()
    // transform AffineTransform's transformation matrix to multiply with the buffered image.
    // Therefore those methods are called in a counterintuitive sequence.
    AffineTransform transform;
    if (rotation % 180 == 0)
    {
        // First scale and second rotate image
        transform = AffineTransform.getRotateInstance(Math.toRadians(rotation), scaledWidth/2, scaledHeight/2);
        transform.scale(scale, scale);
    }
    else
    {
        // First scale, second rotate, and third translate image
        transform = AffineTransform.getTranslateInstance((scaledHeight-scaledWidth)/2, (scaledWidth-scaledHeight)/2);
        transform.rotate(Math.toRadians(rotation), scaledWidth/2, scaledHeight/2);
        transform.scale(scale, scale);
    }
    AffineTransformOp operation = new AffineTransformOp(transform, AffineTransformOp.TYPE_BICUBIC);
    BufferedImage transformedImage = operation.createCompatibleDestImage(image, image.getColorModel());
    return operation.filter(image, transformedImage);
}
Daniel
  • 855
  • 10
  • 21
  • Note that you probably prefer to use Java-native image scaling prior to the AffineTransform (and remove the scale() calls here), otherwise you will lose image quality. – Daniel Nov 14 '13 at 21:34