Search code examples
javaimagerotationjpanelcrop

Java, JPanel crop image while rotating


i am making simple image editor in java. My problem is that when i rotate image its being cropped, i will try to show it on images:

before rotation https://i.sstatic.net/Rk2Un.jpg

after 45 degre https://i.sstatic.net/phUuZ.jpg

after 90 degrees rotation i[dot]imgur[dot]com/4NrABIA.jpg

It looks like JPanel crops rotated image into size of original image, i dont know why :/ so i am asking for help. Thank you.

Heres my code:

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener;
import javax.swing.*;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import static java.awt.image.ImageObserver.WIDTH;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;

public class OknoInterfejsu extends JFrame{

Container content;
BufferedImage image = null;
JLabel picLabel;
Image scaledImage;
private final JButton przyciski[];
private JComboBox wybor;
private String wybor_str[];
private int picHeight;
private int picWidth;
private int realHeight;
double picAspect;
private String path;


private class ObslugaPrzycisku implements ActionListener{

   private final JFrame ref_okno;

   ObslugaPrzycisku(JFrame okno){
        ref_okno = okno;
   }

   public void actionPerformed(ActionEvent e) {
        JButton bt = (JButton)e.getSource();
        if(bt==przyciski[0]){
           //-----------WCZYTAJ PRZYCISK-------------
        {
            JFileChooser fileChooser = new JFileChooser("C:\\Users\\Filip\\Pictures\\");
            FileFilter imageFilter = new FileNameExtensionFilter(
             "Image files", ImageIO.getReaderFileSuffixes());
            fileChooser.setFileFilter(imageFilter);
             if (fileChooser.showOpenDialog(null)==JFileChooser.APPROVE_OPTION)
             {
                 File plik = fileChooser.getSelectedFile();
                 path = plik.getPath();
                 System.out.print(path);
                try {
                    image = ImageIO.read(new File(path));
                } catch (IOException ex) {
                    Logger.getLogger(OknoInterfejsu.class.getName()).log(Level.SEVERE, null, ex);
                }

               picHeight = image.getHeight();
               picWidth = image.getWidth();
               picAspect = (double) picHeight / picWidth;
               //if(picAspect>1)realHeight = 400;
               //else realHeight = 800;
               realHeight = 400;
                scaledImage = image.getScaledInstance(realHeight, (int) (realHeight*picAspect),Image.SCALE_SMOOTH);

                picLabel = new JLabel(new ImageIcon(scaledImage));

                content.add(picLabel);
               content.revalidate();
               content.repaint();

             }

        } 
        }
          //--------------------------OBROT +------------  
         else if(bt==przyciski[1]){
            picLabel.setIcon(null);

            int temp1 = (int) (400*picAspect);

            picRotate(scaledImage, 400,temp1);

            picLabel = new JLabel(new ImageIcon(scaledImage));
               content.add(picLabel);
               content.revalidate();
               content.repaint();
        }   


   }
   }

    public OknoInterfejsu()
   {    
    super("Okno interfejsu");


    setSize(1000,700);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

   //-------------------------------------------------------
    przyciski = new JButton[2];
    przyciski[0] = new JButton("Wczytaj Obraz");
    przyciski[0].addActionListener(new ObslugaPrzycisku(this));

    przyciski[1] = new JButton("Obrót +");
    przyciski[1].addActionListener(new ObslugaPrzycisku(this));

    JPanel panelPrzyciski   = new JPanel(new FlowLayout());
    panelPrzyciski.add(przyciski[0]);
    panelPrzyciski.add(przyciski[1]);

    //-------------------------------------------------------
    content  = getContentPane();
    content.setLayout(new BorderLayout());
    content.add(panelPrzyciski,BorderLayout.SOUTH);

    setVisible(true);
    }



     public static void main(String args[]){
     new OknoInterfejsu();
    }

    public void picRotate(Image image,int w,int h){

    BufferedImage temp=(BufferedImage)createImage(w,h);
    Graphics2D  g2d=(Graphics2D)temp.createGraphics(); 
    g2d.rotate((Math.PI/2)*22.5, w/2, h/2);

    g2d.drawImage(image,0,0,null); 
    this.scaledImage=temp; 
    g2d.dispose();  


    }
    }

Solution

  • When you rotate an image, it changes size, you need to create a new image whose bounds encompass this new size, for example

    public Image rotateBy(Image image, double degrees) {
    
        // The size of the original image
        int w = source.getWidth();
        int h = source.getHeight();
        // The angel of the rotation in radians
        double rads = Math.toRadians(degrees);
        // Some nice math which demonstrates I have no idea what I'm talking about
        // Okay, this calculates the amount of space the image will need in
        // order not be clipped when it's rotated
        double sin = Math.abs(Math.sin(rads));
        double cos = Math.abs(Math.cos(rads));
        int newWidth = (int) Math.floor(w * cos + h * sin);
        int newHeight = (int) Math.floor(h * cos + w * sin);
    
        // A new image, into which the original can be painted
        BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = rotated.createGraphics();
        // The transformation which will be used to actually rotate the image
        // The translation, actually makes sure that the image is positioned onto
        // the viewable area of the image
        AffineTransform at = new AffineTransform();
        at.translate((newWidth - w) / 2, (newHeight - h) / 2);
    
        // And we rotate about the center of the image...
        int x = w / 2;
        int y = h / 2;
        at.rotate(rads, x, y);
        g2d.setTransform(at);
        // And we paint the original image onto the new image
        g2d.drawImage(image, 0, 0, null);
        g2d.dispose();
    
        return rotated;
    }
    

    Then you just need to call it...

    picLabel.setIcon(new ImageIcon(rotateBy(scaledImage, 22.5)));
    

    Because setIcon is a bound property, it should trigger a layout and paint pass automatically, if not, you can simply call revalidate and repaint to trigger them manually