GeeCON Prague 2014*
The moose likes Swing / AWT / SWT and the fly likes Aspect ratio-preserving image scaling, drawing and infinite loops Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Aspect ratio-preserving image scaling, drawing and infinite loops" Watch "Aspect ratio-preserving image scaling, drawing and infinite loops" New topic
Author

Aspect ratio-preserving image scaling, drawing and infinite loops

Joe McCarthy
Greenhorn

Joined: Sep 28, 2005
Posts: 14
Hi,

I want to be able to resize an image to fit within the bounds of a component (e.g., a JPanel) while preserving the original aspect ratio.

To experiment, I adapted an example from the Head First Java (2ed) book, SimpleGui2B (p. 365), and wrote a method for MyDrawPanel to determine the dimensions of the image and the enclosing JPanel, and then adjust the height or width of the image using getScaledInstance.

When I run the code, I encounter an infinite loop whenever I call drawImage on the scaledImage. Simply calling the scaleImage method does not cause any problems if I do not try to draw the resulting image. I modified the code so that it will draw the original image if run with no arguments, and draw the scaled image if run with any arguments. The image file is a 480x480 JPG, and the JFrame within which I am drawing it is 300x400.

I'm fairly new to Java, so I may be missing something fairly obvious regarding paintComponent and/or drawImage. Any relevant insights / experiences / solutions are welcome -- thanks!

Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
You only need to load the image one time. And if you are going to scale
the image you only need to scale it one time. It is inefficient to load
the image and scale it every time the component is asked to render itself.
You can load and scale at construction or as new mages are passed in for
rendering.

edit: adjusted width of comments
[ October 13, 2005: Message edited by: Craig Wood ]
Joe McCarthy
Greenhorn

Joined: Sep 28, 2005
Posts: 14
Craig: thanks for the reply, the information and the code modifications!

I didn't know getScaledInstance was an asynchronous method, so am glad to have a better way to accommodate that, and I hadn't considered pulling the image scaling out of the paintComponent method, so those are both welcome changes.

When I compile and run the code you provided, it does not have an infinite loop, and I am grateful to have that problem resolved. Unfortunately, the image does not get scaled when I supply an additional parameter (e.g., "java SG2B2 t"). The System.out.println() traces I inserted show that the calls to getWidth() and getHeight() in the scaleImage method are now returning 0 (zero) values rather than 292 and 366, respectively, that were returned when my original code is executed, and so no scaling is performed.

As so often happens, this, in turn, provided an unanticipated learning opportunity about Java and arithmetic: the widthRatio and heightRatio computations both involved dividing by zero, and yielded "-Infinity" results (rather than resulting in a run-time error).

I tried to figure out why getWidth() and getHeight() work differently in the two versions of the code, but to no avail. Any further assistance will be further appreciated. Thanks!

Oh, BTW, in case it's of use, the 480x480 JPEG file I am using can be found here: http://interrelativity.com/Logo.jpg
[ October 16, 2005: Message edited by: Joe McCarthy ]
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
The image is being scaled before the component is realized, ie, before MyDrawPanel is
rendered in its parent JFrame. When it is rendered its 'paintComponent' method is called.
When this happens we can get the real width and height of the parent which in this case is
the center section of the BorderLayout of the JFrame. Before either 'pack' or
'setVisible' is called on the JFrame the width and height of MyDrawPanel will be zero.

How to deal with this. I can think of two options. One is to either pass in width and
height values to MyDrawPanel for the paneWidth and paneHeight values used inside the
'scaleImage' method or set them inside MyDrawPanel. Then you can scale the image before
the component (MyDrawPanel) is realized (ie, before a call to either 'pack' or
'setVisible').

The other is to wait until the first trip through the 'paintComponent' method to scale the
image and make sure it is scaled only once. You may come up with some other
options/variations. There are lots of ways to put things together.
Here are the changes to MyDrawPanel with one way to implement the second option.

Thanks for the image link.
Joe McCarthy
Greenhorn

Joined: Sep 28, 2005
Posts: 14
That did the trick -- thanks!

I can see, now, that the reason that my earlier version "worked" (w.r.t. scaling) was due to the infinite loop -- eventually, the panel had a size, and so the image was correctly resized (er, repeatedly, forevermore).
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Aspect ratio-preserving image scaling, drawing and infinite loops