1

I'll start of by showing examples of what's wrong then I'll explain how, and finally I'll ask my question.

This is the picture I want to rotate.

Just and example picture for stackoverflow

I am rotating it 90 degrees and 270 degrees, on multiple occasions and then combining
those into a big buffered-image.

The code I am using to rotate a single bufferedImage is this:

public static BufferedImage rotate(BufferedImage img, int angle) {  
    int w = img.getWidth();  
    int h = img.getHeight();  

    BufferedImage dimg = new BufferedImage(w, h, img.getType());

    Graphics2D g = dimg.createGraphics();
    g.rotate(Math.toRadians(angle), w/2, h/2);  
    g.drawImage(img, null, 0, 0);  
    return dimg;  
}

The out come of the rotation looks something like this.

Rotation with native width and height

The reason those black bars are these is because in the code you can clearly see I create a separate buffered-image which will be the final image.

Which uses the original width and hight, since the image is rotated the with and height switch so I compensated for this by changing BufferedImage dimg = new BufferedImage(w, h, img.getType()); to BufferedImage dimg = new BufferedImage(h, w, img.getType());.

I though it would be logical that this would solve my problem.
But I was wrong now the rotational outcome is this.

rotation with with and height switch So from this point on is where I have no clue why it's doing this.
I might just be overlooking a tiny thing, or it's a common error even though I can't find
any instance of this occurring.

So here is my question to you, why does it do this? And how do I fix this.

Maarten
  • 635
  • 1
  • 9
  • 29

3 Answers3

2

The image isn't square. If you rotate it by 90°, then you will create a gap that you need to fill.

Solutions:

  • Make sure the image is square
  • "Rotate" the size: When you rotate by 90° or 270°, you need to create a target image with swapped width and height (i.e. 200x100 -> 100x200)
  • Crop the image. Good in your case since scaling will make the arrow look bad but it might be out of center
  • Scale the image. If it's 609x579, scale it down to 579x579 (scaling down will usually look a little bit better).
  • Find the border color and fill the gap with the border color after the rotation
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • The problem is it needs to be independent of size, or better to be said it needs to be able to have a correct outcome what ever the size, square or not square. The main reason is the sizes of the individual images within the bigger final image, is done by an complex calculation depending on a load of user information. If I were to crop or scale the image, the problem will be that the position of said images won't be at the correct position relative to the paper. This would mean I would have to change several things. What I want, is when rotating correct for not being square. – Maarten Nov 18 '13 at 11:47
  • 1
    In that case, you need to rotate the image but also "rotate" the size of the final image. I.e. when you rotate by 90° or 270°, you need to create a target image with swapped width and height. (i.e. 200x100 -> 100x200) – Aaron Digulla Nov 18 '13 at 12:47
  • The second example is swapped width and height – Maarten Nov 18 '13 at 12:53
  • So what was the problem? – Aaron Digulla Nov 18 '13 at 13:03
2

if you want to use a similar code as first code this may help ( if you remove the comments and debug lines (such as painting the background) it has only the translate((W-w)/2,(H-h)/2) line in addition )

// do not forget to import static java.lang.Math.*
public static BufferedImage rotate(BufferedImage img, int angle) {  
    int w = img.getWidth(null);  
    int h = img.getHeight(null);  
    double rad = toRadians(angle);
    double eps = 1e-3;
    int W=(int)(abs(cos(rad))*w+abs(sin(rad))*h-eps)+1;//W after rotation(calculated by using a little geometry ) 
    int H=(int)(abs(sin(rad))*w+abs(cos(rad))*h-eps)+1;//H after rotation
    //you may use max value ( diameter of the rectangle ) instead of dynamic value but in that case you must be careful of the black edges ( in this case red edges ) 
    // if 90 is not a divisor of angle then you can't fit a rectangle with that angle in another one so the red edges are inevitable 
    // but with calculated W and H this edges are minimum 

    BufferedImage dimg = new BufferedImage(W,H, BufferedImage.TYPE_INT_RGB);// you can change it to any type you want it's just a sample 
    Graphics2D g = dimg.createGraphics();
    g.setColor(Color.RED); // background color of red for displaying the red edges when image is not completely fit 
    g.fillRect(0, 0, W, H);
    int x=(W-w)/2;   
    int y=(H-h)/2;  
    g.translate(x, y); // moving dimg center to img center ( this was what first code lack in  ) 
    g.rotate(-rad, w/2, h/2);  // now rotating dimg around the center of img ( which is now same as center of dimg )
    // we rotate dimg by -rad and draw img normally , it's like rotating img by rad instead of dimg by -rad
    g.drawImage(img,null,0,0);  // and drawing
    return dimg;  
}
hhoomn
  • 375
  • 4
  • 15
1

I figured it out.

The thing I was doing in the start was rotating the host image (dimg),
and then drawing the original image to it.
I could just as well have tried to fit a square in a circle my earlier rotation actually
makes no sense at all.

So what I need to do is first create the host, draw the image to the host, the rotate the
host and return it as the final image.

public static BufferedImage rotate(BufferedImage img, int angle) {  
    int w = img.getWidth();  
    int h = img.getHeight();  

    BufferedImage dimg = new BufferedImage(w, h, img.getType());

    Graphics2D g = dimg.createGraphics();
    g.drawImage(img, null, 0, 0); //Draw before rotating
    g.rotate(Math.toRadians(angle), w/2, h/2); //Rotating after drawing
    return dimg;  
}

I hope this helps out some other people as well

Maarten
  • 635
  • 1
  • 9
  • 29