Solved

Java resize image to fit JPanel

Posted on 2007-11-27
9
10,573 Views
Last Modified: 2008-02-01
Hi there,

I'm playing about with Java and I'm trying to create a JPanel that displays a given image.  This image should resize as the panel changes size while maintaining the aspect ratio of the image.

Currently I have the given code for the JPanel.  An instance of this is created and displayed within a JFrame but when I resize the frame the image does not correctly scale to take up the width/height of the panell.  I think that I may have something wrong with getting the scale but I'm a little stuck.

Any help would be appreciated.

Also - does the scaling of the buffered image change the size of the image in the actual memory?  Maybe doesn't make sense but should I copy the buffered image, as shown in code, and resize the copy.  My thinking is that if it is scaled down to 10px by 10px then a rescale to 100px by 100px will have really poor quality - unless i reload the original.

Thanks.
public class ImagePanel 

extends JPanel

{

    private String m_ImageFile;             // the path and name of the image file to be displayed

    private BufferedImage m_Image;          // instance of the image to be displayed

    private BufferedImage m_ScaledImage;    // scaled instance of the image to be displayed

    

    /** Creates a new instance of ImagePanel */

    public ImagePanel(String imageFile) {

        if (imageFile == null) throw new NullPointerException("Image file should not be a null value.");

        this.m_ImageFile = imageFile;

        

        InitComponents();

    }

    

    /** Initialise the class components */

    private void InitComponents() {

        // try to load the image file

        try {

            m_Image = ImageIO.read(new File(m_ImageFile));

        } catch (IOException ex) {

            ex.printStackTrace();

        }

    }
 

    /** Calculate the scale required to correctly fit the image into panel */

    private double GetScale(int panelWidth, int panelHeight, int imageWidth, int imageHeight) {

        double scale = 1;

        double xScale;

        double yScale;

        

        if (imageWidth > panelWidth || imageHeight > panelHeight) {

            xScale = (double)imageWidth  / panelWidth;

            yScale = (double)imageHeight / panelHeight;

            scale = Math.min(xScale, yScale);

        }

        else if (imageWidth < panelWidth && imageHeight < panelHeight) {

            xScale = (double)panelWidth / imageWidth;

            yScale = (double)panelHeight / imageHeight;

            scale = Math.min(xScale, yScale);

        }

        else {

            scale = 1;

        }
 

        return scale;

    }

    

    /** Override paint method of the panel */

    public void paint(Graphics g) {

        if( m_Image != null) {

            super.paintComponent(g);
 

            Graphics2D g2 = (Graphics2D)g;

            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
 

            m_ScaledImage = m_Image;

            

            // Get the required sizes for display and calculations

            int panelWidth = this.getWidth();

            int panelHeight = this.getHeight();

            int imageWidth = m_ScaledImage.getWidth();

            int imageHeight = m_ScaledImage.getHeight();

            

            double scale = GetScale(panelWidth, panelHeight, imageWidth, imageHeight);

            

            /**

            System.out.println("---------------------------------------");

            System.out.println("Panel: "+this.getName()+".");

            System.out.println("Size: "+panelWidth+", "+panelHeight+".");

            System.out.println("Image size: "+m_ScaledImage.getWidth()+", "+m_ScaledImage.getHeight()+".");

            System.out.println("Scale: "+scale+".");

             **/

            

            // Calculate the center position of the panel -- with scale

            double xPos = (panelWidth - scale * imageWidth)/2;

            double yPos = (panelHeight - scale * imageHeight)/2;
 

            // Locate, scale and draw image

            AffineTransform at = AffineTransform.getTranslateInstance(xPos, yPos);

            at.scale(scale, scale);

            g2.drawRenderedImage(m_ScaledImage, at);

        }

    }    

}

Open in new window

0
Comment
Question by:VanLouin
  • 3
  • 2
  • 2
  • +1
9 Comments
 
LVL 24

Expert Comment

by:sciuriware
ID: 20358255
If you know the dimensions of the JPanel you can easily resize the picture before display:

   /**
    * Resizes an Image by stretching it to a new size by x factor and y factor.
    *
    * @param picture the initial image.
    * @param xFactor the width stretching factor.
    * @param yFactor the height stretching factor.
    * @return the stretched image.
    */
   public static Image resize(Image picture, double xFactor, double yFactor)
   {
      BufferedImage buffer;
      Graphics2D g;
      AffineTransform transformer;
      AffineTransformOp operation;

      buffer = new BufferedImage
      (
         picture.getWidth(null),
         picture.getHeight(null),
         BufferedImage.TYPE_INT_ARGB
      );
      g = buffer.createGraphics();
      g.drawImage(picture, 0, 0, null);
      transformer = new AffineTransform();
      transformer.scale(xFactor, yFactor);
      operation = new AffineTransformOp(transformer, AffineTransformOp.TYPE_BILINEAR);
      buffer = operation.filter(buffer, null);
      return(Toolkit.getDefaultToolkit().createImage(buffer.getSource()));
   }

;JOOP!
0
 
LVL 24

Expert Comment

by:sciuriware
ID: 20358257
And don't forget some imports:

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;

;JOOP!
0
 

Author Comment

by:VanLouin
ID: 20358311
Currently the image is being resized but it doesn't appear to be acting like I expect it to.

I want the image to stretch to be the same size as one of the panels axis while maintaining the aspect ratio.  The size of the panel is obtained by this.getWidth() and this.getHeight()  - I considered that there may be a problem here due to the panel being resized as I drag the parent JFrame size.  No idea how to check this or other ways to approach it though.

I'm thinking that the problem is either with my GetScale method having wonky logic or it's to do with getting the size of the panel.

Thanks.
0
 
LVL 24

Expert Comment

by:sciuriware
ID: 20358421
If you change the frame size: monitor it with a listener on the frame itself:

see Interface ComponentListener
and its method: componentResized()

;JOOP!
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
LVL 92

Accepted Solution

by:
objects earned 500 total points
ID: 20361418
should be able to adapt this:

http://www.objects.com.au/java/qa/1959359983.html

just need to change it to retain aspect ratio
0
 

Author Comment

by:VanLouin
ID: 20364261
Some further investigation and help from the Sun forum has highlighted that I should be overriding the paintComponent() method and not the paint() method for custom painting.  I've no idea why but seemingly I was all wrong on this - maybe something for me to read up on!

The way I was calculation the scaling based on the panel size was also wrong.  An adjustment to the scaling method fixed this so I've attached the code for anyone that wants it.

Thanks anyway guys.
public class ImagePanel 

extends JPanel

{

    private String m_ImageFile;             // the path and name of the image file to be displayed

    private BufferedImage m_Image;          // instance of the image to be displayed

    private BufferedImage m_ScaledImage;    // scaled instance of the image to be displayed

    

    /** Creates a new instance of ImagePanel */

    public ImagePanel(String imageFile) {

        if (imageFile == null) throw new NullPointerException("Image file should not be a null value.");

        this.m_ImageFile = imageFile;

        

        InitComponents();

    }

    

    /** Initialise the class components */

    private void InitComponents() {

        // try to load the image file

        try {

            m_Image = ImageIO.read(new File(m_ImageFile));

        } catch (IOException ex) {

            ex.printStackTrace();

        }

    }
 

    /** Calculate the scale required to correctly fit the image into panel */

     private double GetScale(int panelWidth, int panelHeight, int imageWidth, int imageHeight) {

        double scale = 1;

        double xScale;

        double yScale;

 

        // should check that denom != 0 first.

        xScale = (double) panelWidth / imageWidth;

        yScale = (double) panelHeight / imageHeight;

        scale = Math.min(xScale, yScale);

        return scale;

    }

    

    /** Override paint method of the panel */

    public void paintComponent(Graphics g) {

        if( m_Image != null) {

            super.paintComponent(g);
 

            Graphics2D g2 = (Graphics2D)g;

            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
 

            // Grab a copy of the original image for scaling

            m_ScaledImage = m_Image;

            

            // Get the required sizes for display and calculations

            int panelWidth = this.getWidth();

            int panelHeight = this.getHeight();

            int imageWidth = m_ScaledImage.getWidth();

            int imageHeight = m_ScaledImage.getHeight();

            

            // Get the scale that the image should be resized with

            double scale = GetScale(panelWidth, panelHeight, imageWidth, imageHeight);

                     

            // Calculate the center position of the panel -- with scale

            double xPos = (panelWidth - (scale * imageWidth))/2;

            double yPos = (panelHeight - (scale * imageHeight))/2;
 

            // Locate, scale and draw image

            AffineTransform at = AffineTransform.getTranslateInstance(xPos, yPos);

            at.scale(scale, scale);

            g2.drawRenderedImage(m_ScaledImage, at);

        }

    }    

}

Open in new window

0
 
LVL 92

Expert Comment

by:objects
ID: 20364573
that can be simplified a bit, see the code i posted in my earlier comment
0
 
LVL 1

Expert Comment

by:Vee_Mod
ID: 20390963
Force accepted.
Vee_Mod
Community Support Moderator
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Java contains several comparison operators (e.g., <, <=, >, >=, ==, !=) that allow you to compare primitive values. However, these operators cannot be used to compare the contents of objects. Interface Comparable is used to allow objects of a cl…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
This tutorial will introduce the viewer to VisualVM for the Java platform application. This video explains an example program and covers the Overview, Monitor, and Heap Dump tabs.

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

22 Experts available now in Live!

Get 1:1 Help Now