Background in JPanel repaint - java

I have a program that has multiple layers, in the bottom layer I have a JPanel that I have put a background image on. On top of this I have a JLayeredPane that has some draggable components. My problem is that when the user have uploaded a background image the draggable components are really lagging, my guess is that it's because of that swing is repainting the background image al the time when dragging. My question is if its anyway to make sure the image is not repainting all the time?
My code where I paint the image looks like this:
if (this.getLoadedBackgroundImage() != null) {
try {
BufferedImage bufferedImage = ImageIO.read(this.getLoadedBackgroundImage());
Image bImage = bufferedImage.getScaledInstance(this.getWidth() - 5 - jPanel6.getWidth() -5, this.getHeight() - jPanel2.getHeight() - jPanel3.getHeight() - tabPanel.getHeight(), Image.SCALE_FAST);
graphics2d.drawImage(bImage, 5, jPanel2.getHeight() + tabPanel.getHeight()+5, null);
} catch (IOException ex) {
Logger.getLogger(MainViewLayeredPane.class.getName()).log(Level.SEVERE, null, ex);
}
}

Don't load or resize the image the paintComponent method, these are expensive operations, instead, pre-cache it and only paint the cached version

Related

Scaling an image on a button which is resized

I'm working on an application which allows a used to place controls, move, resize, etc. But I'm trying to add icon images to a button control. When placed and instanced, it resizes the icon image per the code below.
But when I resize the control using user features and it calls this routing again, it fails to resize the image and it remains the original icon size. I've tried using "this.", passing the control to itself, I've done prints to ensure it's seeing the new size and width... what I am missing?
Also, when I create a 2nd control (or 3rd, etc), it uses the 1st image's initial size.
Thanks!
protected void sizeIcon () {
try {
File f2 = new File("media\\button.gif");
BufferedImage inputImage = ImageIO.read(f2);
BufferedImage img = new BufferedImage(this.getWidth(), this.getHeight(), inputImage.getType());
Graphics2D g = img.createGraphics();
g.drawImage(inputImage, 7, 0, this.getWidth(), this.getHeight(), null);
ImageIO.write(img, "gif", new File("test.gif"));
this.setIcon(new ImageIcon("test.gif"));
g.dispose();
} catch(Exception e) {System.out.println(e);}
Sorry, got it, appears the old file was not being replaces.
-MH

Thread.sleep not displaying image properly in paintComponent

When drawing a BufferedImage , the transition image does not display. I am trying to create a game in which when the user wins, it displays a loading transitional screen and then proceeds to the img2 screen. I want to create a 1 or 2 second delay to make it seem as though the second level is in the process of loading however when I add thread.sleep it delays it but does not display the transition image, instead, it just jumps straight toimg2. I have tried creating separate flags,methods and increasing the time but nothing seems to work!
Below is the paintComponenetmethod (I have tried separating into separate methods):
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(img, 0, 0, this);
if (win1 == true) {
try {
Thread.sleep(1000);
} catch (Exception exc) {
}
g2d.drawImage(transition, 0, 0, this);
try {
Thread.sleep(1000);
} catch (Exception exc) {
}
g2d.drawImage(img2, 0, 0, this);
}
}
}
An explanation would be very helpful as well!
All painting is done on the Event Dispatch Thread.
When you invoke Thread.sleep() in the paintComponent() method the GUI can't repaint itself until all the code is finished executing which means only the last image will ultimately get painted as the two paint requests will be combined into one. Read the section from the Swing tutorial on Concurrency for more information.
Don't use Thread.sleep() in a painting method.
The better solution is to not even do custom painting. Instead you can just use a JLabel and change the Icon of the label.
Then you can use use a Swing Timer to schedule the animation. So you would set the Icon and then set the Timer to fire in 2 seconds. When the Timer fires you reset the Icon of the label.

Resize and Redraw a image in a JPanel

I'm creating a simple java program to load a image and zoom in/out the image.
So far I have been able load the picture and zoom it. But when drawing the zoomed out image I can still see the previous image on the JPanel.
Code for loading the image
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileFilter(new FileNameExtensionFilter("Images(jpg,png,gif)", "jpg","png","gif"));
int opt = fileChooser.showOpenDialog(this);
if(opt == JFileChooser.APPROVE_OPTION){
String filePath = fileChooser.getSelectedFile().getAbsolutePath();
try {
bufImage = ImageIO.read(fileChooser.getSelectedFile());
} catch (IOException ex) {
Logger.getLogger(ImageZoom.class.getName()).log(Level.SEVERE, null, ex);
}
image = new ImageIcon(filePath);
imgWidth = image.getIconWidth();
imgHeight = image.getIconHeight();
imagePanel.getGraphics().drawImage(image.getImage(), 0, 100, image.getIconWidth(), image.getIconHeight(), imagePanel);
}
Code for zooming in
double scale_factor = 1.5;
imagePanel.getGraphics().drawImage(image.getImage(), 0, 100, (int)(imgWidth*=scale_factor), (int)(imgHeight*=scale_factor), imagePanel);
Code for zooming out
double scale_factor = 0.5;
imagePanel.getGraphics().drawImage(image.getImage(), 0, 100, (int)(imgWidth*=scale_factor), (int)(imgHeight*=scale_factor), imagePanel);
Can some one please suggest a way to only have the resized image on the panel?
This is how zooming out looks like. same problem occurs when opening a new picture.
I tried repainting the panel but then everything vanishes.
That's because imagePanel.getGraphics() isn't how custom painting works in Swing. Instead, create a custom class which extends from something like JPanel and override it's paintComponent method, painting your scaled image there (making sure to call super.paintComponent first)
See Painting in AWT and Swing and Performing Custom Painting for more details
For example:
zoom using mouse and graphics
Java image zooming japplet
Jpanel resize on repaint

Preventing RootPane repaint on GlassPane repaint

This question is similar to Paint on a glass pane without repainting other components. But I can't figure out how to apply the solution offered there.
My problem is that we have a very complicated RootPane, lots of components, and repainting it is expensive. We also have an animation running on the JFrame's GlassPane. I've noticed that every tick of the animation is repainting the animation as it should, but then also causing everything on the underlying RootPane to also be repainted.
I've tried overriding the paint() method for the RootPane with various code, but it always causes the components on the RootPane to be wiped and lots of flickering, probably from when child components try to repaint themselves as things update during the animation:
pnlContent = new JRootPane() {
#Override
public void paint(Graphics g) {
OurGlassPaneWithAnimation glass = ...
if (glass != null && glass.animation != null && glass.animation.isAlive()) {
//Animation running, do something cool so root pane still looks good without repainting out the wazoo
} else { super.paint(g); }
}
};
Maybe putting the animation in a GlassPane is a bad idea? We've though about changing it to be in centered JPanel instead. Or is there a good way to do this using GlassPane and keeping the repaints done to the background RootPane to a minimum?
#MadProgrammer Thanks for the suggestion doing a repaint(rect) instead of just a repaint() solved the problem. Now we can crank up the animation fps as high as we want and it doesn't noticeably affect the load time of the RootPane behind it. Here's the code snippet.
while (keepOnAnimating) {
BufferedImage bi = vFrames.elementAt(0);
if (bi != null) {
// This only repaints area of image rect
// Prevents lots of repaints from happening in rootpane behind glasspane.
int x = getWidth() / 2 - bi.getWidth() / 2;
int y = getHeight() / 2 - bi.getHeight() / 2;
jc.repaint(x, y, bi.getWidth(), bi.getHeight());
} else {
jc.repaint();
}
...
}

How to save JPanel as JPEG with its components

I need to save a chessboard as a jpeg file. I know how to save a java component into JPEG. But my problem is that I use JButtons as cells and when I try to save the whole chessboard, only the panel is saved. Does anybody know how to save a JComponent WITH ITS CHILD COMPNENTS into JPEG (png etc.) Thank you in advance.
You can print a Component (such as JPanel) to any Graphics object. So, why not use the Graphics object of a BufferedImage and write it to disk.
void takePicture(JPanel panel) {
BufferedImage img = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_RGB);
panel.print(img.getGraphics()); // or: panel.printAll(...);
try {
ImageIO.write(img, "jpg", new File("panel.jpg"));
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
This makes this JPanel
JPanel panel = new JPanel();
panel.add(new JButton("Hello"));
panel.add(new JButton("World"));
Look like this:
updated based on comments, many thanks to #MadProgrammer
Check out Screen Image. It will allow you to create an image of any component in the frame. If you choose to make an image of a panel all the child components are included in the image.
Use Robot class to create a screen capture of your screen.
Use this tutorial for help:
Screen Capture using Robot class
Now for your chessboard, clip the image by calling subImage method of BufferedImage class.
Here's the link on clipping BufferedImage
You can get Dimensions of you JPanel by calling getX(), getY(), getWidth() and getHeight() methods which you can pass into subImage method.
Manipulate the passing value by adding/substracting some value to get desired results.

Categories

Resources