7

Same question as last time but I will provide more detail. I am currently rotating images using:

 int rotateNum //in main class

 double rotationRequired = Math.toRadians(rotateNum);

    double locationX = img.getWidth(this) / 2;
    double locationY = img.getHeight(this) / 2;
    AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
    AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);


    g2d.drawImage(op.filter((BufferedImage)img, null), imgX, imgY, null);

And then I am actually rotating the image using:

double deltaX = (double)(imgY - otherImg.imgY);
double deltaY = (double)(imgX - otherImg.imgX);
rotateNum = (int)(180 * Math.atan2(deltaY, deltaX) / Math.PI);

My images vary in size. The smaller images don't get cut off (meaning cut off with white space) but the larger ones do, on the left or right side. Resizing the images doesn't work, and I clipped out the white rectangle around the image using the GIMP.

Example Images: Before(ignore the grey area to the left)

After: See the cutoff at the side

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Jimmt
  • 852
  • 2
  • 11
  • 29

4 Answers4

3

The problem is your source image is not exactly quadratic. When you implement the AffineTransform rotation with at.rotate(-rad, width/2, height/2);, it is the same as:

at.translate(width/2,height/2);
at.rotate(rads);
at.translate(-width/2,-height/2);

So, when it execute the last line, it translates to the origin. And if the width is greater than y (or vice versa), than the origin of the transform will be translated to a smaller distance than the side of greater length.

For example, if your width is 30 and your height is 60, than the origin point will be set as (-15,-30) from where the transform was original set. So, when you translate it, say, 90 degrees, the image will end up with "width" 60 and "height" 30, but according to the origin point, the image original bottom will be drawn at (-30,0), so it overflows the AffineTransform in -15 in X axis. Then this part of image will cut.

To correct this, you can use the following code instead:

double degreesToRotate = 90;
double locationX =bufferedImage.getWidth() / 2;
double locationY = bufferedImage.getHeight() / 2;

double diff = Math.abs(bufferedImage.getWidth() - bufferedImage.getHeight());

//To correct the set of origin point and the overflow
double rotationRequired = Math.toRadians(degreesToRotate);
double unitX = Math.abs(Math.cos(rotationRequired));
double unitY = Math.abs(Math.sin(rotationRequired));

double correctUx = unitX;
double correctUy = unitY;

//if the height is greater than the width, so you have to 'change' the axis to correct the overflow
if(bufferedImage.getWidth() < bufferedImage.getHeight()){
    correctUx = unitY;
    correctUy = unitX;
}

int posAffineTransformOpX = posX-(int)(locationX)-(int)(correctUx*diff);
int posAffineTransformOpY = posY-(int)(locationY)-(int)(correctUy*diff);

//translate the image center to same diff that dislocates the origin, to correct its point set
AffineTransform objTrans = new AffineTransform();
objTrans.translate(correctUx*diff, correctUy*diff);
objTrans.rotate(rotationRequired, locationX, locationY);

AffineTransformOp op = new AffineTransformOp(objTrans, AffineTransformOp.TYPE_BILINEAR);

// Drawing the rotated image at the required drawing locations
graphic2dObj.drawImage(op.filter(bufferedImage, null), posAffineTransformOpX, posAffineTransformOpY, null);

Hope it help.

Lucas Borsatto
  • 183
  • 1
  • 10
  • In your answer @LucasBorsatto you have 2 magic numbers: posX and posY. To what are those referring and where are they obtained? – PGMacDesign May 21 '18 at 14:59
2

I imagine that it's not the size of the image that matters but rather its eccentricity: images that are more square-like have less of a problem then images that are either more fat or more thin.

I think that your problem is that your center of rotation shouldn't be [width / 2, height / 2] -- it's not that simple. Instead think of the image residing in the left upper portion of a large square the length of the square's side will be the image's width or height, whichever is larger. This is what gets rotated whenever you rotate your image.

For example, please see my reply here: https://stackoverflow.com/a/8720123/522444

Community
  • 1
  • 1
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
1

This is something that java does unfortunately. One way to solve it is to make the shape a square, so that when rotating no clipping occurs.

This problem is covered in David's "Killer game programming in Java" book, books_google_killer+game+programming+clipping+rotating which is a great book if you want to do any java game programming (Even if it is a bit old).

Edit :: This converting of an image to a square can either be done to the raw image through image editing software, or through java itself. Perhaps roll your own rotating method which can check for such collisions..

Chandra Sekhar
  • 16,256
  • 10
  • 67
  • 90
AlanFoster
  • 8,156
  • 5
  • 35
  • 52
1

Rotating the image may also affect the size of the image. Here is some code I found on the old Sun forums a long time ago (I forget the original poster). It recalculates the size required to display the image at its given angle of rotation:

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import javax.imageio.*;
import javax.swing.*;

public class RotateImage {
    public static void main(String[] args) throws IOException {
        URL url = new URL("https://blogs.oracle.com/jag/resource/JagHeadshot-small.jpg");
        BufferedImage original = ImageIO.read(url);
        GraphicsConfiguration gc = getDefaultConfiguration();
        BufferedImage rotated1 = tilt(original, -Math.PI/2, gc);
        BufferedImage rotated2 = tilt(original, +Math.PI/4, gc);
        BufferedImage rotated3 = tilt(original, Math.PI, gc);
        display(original, rotated1, rotated2, rotated3);
    }

    public static BufferedImage tilt(BufferedImage image, double angle, GraphicsConfiguration gc) {
        double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle));
        int w = image.getWidth(), h = image.getHeight();
        int neww = (int)Math.floor(w*cos+h*sin), newh = (int)Math.floor(h*cos+w*sin);
        int transparency = image.getColorModel().getTransparency();
        System.out.println(transparency);
//        BufferedImage result = gc.createCompatibleImage(neww, newh, transparency);
        BufferedImage result = gc.createCompatibleImage(neww, newh, Transparency.TRANSLUCENT);
        Graphics2D g = result.createGraphics();
        g.translate((neww-w)/2, (newh-h)/2);
        g.rotate(angle, w/2, h/2);
        g.drawRenderedImage(image, null);
        return result;
    }

    public static GraphicsConfiguration getDefaultConfiguration() {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        return gd.getDefaultConfiguration();
    }

    public static void display(BufferedImage im1, BufferedImage im2, BufferedImage im3, BufferedImage im4) {
        JPanel cp = new JPanel(new GridLayout(2,2));
        addImage(cp, im1, "original");
        addImage(cp, im2, "rotate -PI/2");
        addImage(cp, im3, "rotate +PI/4");
        addImage(cp, im4, "rotate PI");

        JFrame f = new JFrame("RotateImage");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setContentPane(cp);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    static void addImage(Container cp, BufferedImage im, String title) {
        JLabel lbl = new JLabel(new ImageIcon(im));
        lbl.setBorder(BorderFactory.createTitledBorder(title));
        cp.add(lbl);
    }
}
Luke Woodward
  • 63,336
  • 16
  • 89
  • 104
camickr
  • 321,443
  • 19
  • 166
  • 288