Reputation: 33
I am trying to create a funny game, which includes a Wheel, a ball. Each time the user click on the button, the ball will roll and randomly land on one slot in the wheel.Things are OK, to create the Wheel, I put the image and the ball (Oval Object) on a separate JPanel
. The functional behavior are good as i expect. However, each time i run the program, the CPU sounds loudly and the usage of Java application often take up to 25%.
That's quite terrible. After a while doing some tests, it seems that the error is inside my painComponent()
overridden methods as the Java repeatedly call this method nonstop. A more throughout investigation suggest the the problem is the method getScaledInstance
which is used to scale the image inside panel. It appears to me that if i remove this method, the paintComponent()
will not be called repeatedly.
However, things are I don't know how to make the image scaled without getScaledInstance
. Note that I tried to make a copy based on the sample of MadProgrammer Java: maintaining aspect ratio of JPanel background image
So, what do you think about it? Is there any way to set the background image, keep the aspect ratio and not over calling paintComponent()
method? Note that I did try to use Thread.sleep
but it doesn't work very well.
This is quite struggling for me. Anyway, thank for reading!
public class WheelPanel2 extends JPanel {
private BufferedImage image;
private int time = 0;
private Image scaled = null;
private int scaleWidth = 300;
private int scaleHeight = 300;
public WheelPanel2() {
try {
image = ImageIO.read(new File("img/Basic_roulette_wheel_1024x1024.png"));
scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
} catch (IOException e) {
e.printStackTrace();
}
setSize(400, 400);
setPreferredSize(new Dimension(400, 400));
setBorder(BorderFactory.createLineBorder(Color.BLACK));
double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
Image scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
}
/**
* The Paint Method - Create the image of the Wheel and the Ball
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
//TRUE WIDTH AND HEIGHT
int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - scaled.getWidth(this)) / 2;
int y = (height - scaled.getHeight(this)) / 2;
g.drawImage(scaled, x, y, this);
System.out.println("PAINT");
}
/**
* These two methods are used to automatically resize the wheelpanel and the ball
*/
private double getScaleFactorToFit(Dimension original, Dimension toFit) {
double dScale = 1d;
if (original != null && toFit != null) {
double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);
dScale = Math.min(dScaleHeight, dScaleWidth);
}
return dScale;
}
private double getScaleFactor(int iMasterSize, int iTargetSize) {
double dScale;
if (iMasterSize > iTargetSize) {
dScale = (double) iTargetSize / (double) iMasterSize;
} else {
dScale = (double) iTargetSize / (double) iMasterSize;
}
return dScale;
}
public static void main (String[] args) {
JFrame frame = new JFrame("HI THERE");
frame.setSize(1000, 1000);
frame.setLayout(new BorderLayout());
frame.add(new WheelPanel2(), BorderLayout.CENTER);
frame.setVisible(true);
frame.setDefaultCloseOperation(1);
}
}
If you try to run the program, you will know that the paintComponent are called non-stop.
Upvotes: 0
Views: 429
Reputation: 44338
Rather than scaling the Image yourself, let the Graphics do it, by calling the drawImage method which takes width and height arguments:
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = image.getWidth(this);
int height = image.getHeight(this);
float scale = Math.min(
(float) getWidth() / width,
(float) getHeight() / height);
width = (int) (width * scale);
height = (int) (height * scale);
int x = (getWidth() - width) / 2;
int y = (getHeight() - height) / 2;
g.drawImage(image, x, y, width, height, this);
}
On most platforms, this will take advantage of the system’s graphics acceleration to scale the image.
If you want to be certain that the equivalent of SCALE_SMOOTH is used to scale the image, set the appropriate RenderingHint:
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D) g).setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
int width = image.getWidth(this);
int height = image.getHeight(this);
float scale = Math.min(
(float) getWidth() / width,
(float) getHeight() / height);
width = (int) (width * scale);
height = (int) (height * scale);
int x = (getWidth() - width) / 2;
int y = (getHeight() - height) / 2;
g.drawImage(image, x, y, width, height, this);
}
Upvotes: 2
Reputation: 824
Hi I don't understand much of a swing but can you try to call getScaledInstance
only one time in the constructor and there to create Image scaled
?
//part of the class properties
private Image scaled = null;
//other properties
public WheelPanel() {
this.this_class = this;
try {
image = ImageIO.read(new File("img/Basic_roulette_wheel_1024x1024.png"));
//Create only one scaled image and after that use it in paintComponent method
scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
} catch (IOException e) {
e.printStackTrace();
}
setSize(400, 400);
setPreferredSize(new Dimension(400, 400));
setBorder(BorderFactory.createLineBorder(Color.BLACK));
}
If this for some reason does not work you can try to create Image scaled
only one inside paintComponent
method. Example:
//part of the class properties
private Image scaled = null;
/**
* The Paint Method - Create the image of the Wheel and the Ball
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
if(scaled == null)
{
scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
}
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - scaled.getWidth(this)) / 2;
int y = (height - scaled.getHeight(this)) / 2;
g.drawImage(scaled, x, y, this_class);
}
Edit: I saw how you modify the example with the constructor. Please try this constructor:
//part of the class properties
private Image scaled = null;
public WheelPanel2() {
try {
image = ImageIO.read(new File("img/Basic_roulette_wheel_1024x1024.png"));
} catch (IOException e) {
e.printStackTrace();
}
setSize(400, 400);
setPreferredSize(new Dimension(400, 400));
setBorder(BorderFactory.createLineBorder(Color.BLACK));
double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
}
Good luck to all!
Upvotes: 0