Java Swing - flickering Canvas graphics - java

I have to write a simple Java app which can load pictures, show it in a GUI form, allow the user to apply some transformation, and show the transformed picture.
My solution is working fine, but the UI is flickering a bit, because the repaint method called too often (for example when the user scaling the image with a JSlider)
My code looks like this:
public class ImageCanvas extends Canvas
{
private BufferedImage image;
// ...
#Override
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
if(image != null)
{
// I draw out the image...
}
}
public void setImage(BufferedImage image)
{
this.image = image;
this.repaint();
}
public void setRotation(double rotation)
{
this.rotation = rotation;
this.repaint();
}
public void setScale(double scaleX, double scaleY)
{
//set the scaling field, then repaint ....
}
// and so on...
}
And, of course, I have an ImageCanvas control on my main UI, and I simply call the public methods (see for example the "setRotation" method above) which repaint the canvas area. I know it's a simple question, but I don't even find a DoubleBuffered property on the Canvas...
Any help appreciated.

Double buffering is built-in for Swing (i.e. JComponent derived) classes.
If you want built-in double-buffering, you should extend JPanel rather than Canvas, and override paintComponent, not paint.

If you can use JPanel than go for it. Please make sure you are not overriding the JPanel.paint method, override JPanel.paintComponent instead.
See this link for details.

Usually graphic lags in these applications can be caused by setting a empty variable at the top of the script, then changing its value, then waiting for the repaint to update it. You could try changing the:
setRotation(double rotation);
so that it rotates the image in that method.
Just a general thing I happen to see while dealing with graphics.

Related

How to paint a JPanel only by a JButton press?

This is a fragment of an application with several tabs hung off a JTabbedPanel:-
I have generated the image with code in the standard following manner:-
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(device.getVisualisation(), 0, 0, null);
}
So every time something happens to the application gui, the paintComponent method is called and the image displays. That's normal.
This paint event also happens if the tab focus changes, or the mouse passes over the tab as you'd expect. The problem is this takes several seconds as generating the graphic takes a lot of time. This delay is unavoidable and I accept it. You also get many paint events as the gui system does what it has to. Normally this would be okay, but with the processing delay, the gui stops /flashes /updates several times over the period of say 10 seconds.
I thought that I could deal with this by only manually calling repaint() from a "REFRESH" JButton somewhere on the gui. But I can't turn off the automatic repainting if you tough the tabs. How do I only paint a component by pressing a button, and not automatically?
I would do things a bit differently by first figuring out what is the expensive process here. It's not painting but rather the calculation and creation of the special image that is displayed by the painting. So with this in mind, rather than turning off painting, which would involve only kludges, instead store the image drawn an image field, and carefully control when it is recreated via the expensive device.getVisualisation() method.
If this method is truly long-running, then it don't call it within paintComponent, a method which never should hold cpu-intense or time crunching code, and in fact, the method should be called off of the Swing event thread, and instead within a background thread. Then when the background thread is done processing, update that same BufferedImage and call repaint(), and display the new image.
For example:
private Image image = null; // holds our image
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (image != null) {
// this will hardly take any time at all to run
g2d.drawImage(image, 0, 0, this);
}
}
public void myDrawImage() {
// create a SwingWorker for background threading work
new SwingWorker<Image, Void>() {
#Override
protected Image doInBackground() throws Exception {
// run this long-running code within this background thread
return device.getVisualisation();
};
#Override
protected void done() {
try {
// when the thread is done, get the new image,
// put it into our image field, and repaint the component
image = get();
repaint();
} catch (InterruptedException | ExecutionException e) {
// TODO handle any exceptions that occur with drawing
}
};
}.execute();
}
Now the image drawn will only change when and if your program specifically calls the myDrawImage() method, so that now the calling of the long-running code is under your total control.

Java Graphics: setClip vs clipRect vs repaint(int,int,int,int)

Similar to my last post apologies, but a lot less long winded. Basically I am wondering what is the best option for optimising redrawing to a JFrame/JPanel when each repaint call only a small part of the screen would be redrawn.
Also apart from overloading repaint I am not 100% on how to implement setClip or clipRect. My understanding they are used when overriding paint or update?
See below for some code:
public class AquaSim extends JPanel {
//variables
//other methods...
public void paint (Graphics g) {
super.paintComponent(g); //needed?
Graphics2D g2d = (Graphics2D) g;
//Draws the land area for penguins.
g2d.setColor(new Color(121,133,60));
g2d.fill(land);
g2d.setColor(Color.BLUE);
g2d.fill(sea);
drawCreatures(g2d);
}
public void drawCreatures(Graphics2D g2d) {
for (Creature c : crlist) //a list of all alive creatures {
//each creature object stores its image and coords.
g2d.drawImage(c.createImage(txs,tys), c.getPos(1).width, c.getPos(1).height, this);
}
}
}
Ideally I would prefer not to have to loop though each creature object each repaint request, which is part of the reason for this post. I dont know if there would be a way of sending the current creature being drawn to paint or overriding paint in the Creature class to get it draw itself on to the graphics object in the main class.
A bit more code...
private class Creature implements ActionListener {
//variables & other methods
#Override
public void actionPerformed(ActionEvent e) {
if (getState()!=State.DEAD) {
move();
repaint(); //<---Would rather set clipping area in paint/update. x,y,w,h needs to include ICON & INFO BOX.
//repaint(gx,gy,getPos(1).width,getPos(1).height);
}
else anim.stop();
}
//...
public void move() {
//Determines how it will move and sets where to here by updating its position that is used in drawCreatures.
}
}
So any suggestions as which would be the most efficient method to use? Baring in mind repaint will be called a lot by many objects/creatures i.e. many times a second, hence why I dont want it redrawing everythin on the screen each repaint request.
only a small part of the screen would be redrawn.
Use repaint(....).
The RepaintManager will worry about what needs to be painted and will set the clip of the Graphics object for you.
Just out of interest is it possible to make the Graphics object attached to the JPanel in AquaSim global and then from within each Creature object use this graphics object to draw to the JPanel?
Although I guess I would need to figure out how to implement/override the paint method within the Creature class a bit like Gilbert is trying to tell me.

Draw on Jframe image

I want to read a file to get some points and then draw these points on an image. Currently, I am able to draw values on the image, but the file is read three times and the rectangles are drawn three times. I don't know where is the problem. Below is the code. The Read() function works fine seperately so I didn't include it in the code.
P.S: I am beginner in JAVA and don't know much about JFrame and Jcomponent.
public class LoadImageApp extends JComponent {
BufferedImage img;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, null);
Read(g);// This is the function in which I read a file.
}
public LoadImageApp() {
try {
img = ImageIO.read(this.getClass().getResource("/New York.jpg"));
} catch (IOException e) {
}
}
public Dimension getPreferredSize() {
if (img == null) {
return new Dimension(100,100);
} else {
return new Dimension(img.getWidth(null), img.getHeight(null));
}
}
public static void main(String[] args) {
JFrame f = new JFrame("Load Image Sample");
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
LoadImageApp img = new LoadImageApp();
f.add(img);
f.pack();
f.setVisible(true);
}
}
Do not, do not, DO NOT read from a file from within any painting method such as paintComponent(...). :)
That method should be for painting only. The more you slow it down, the less responsive your GUI will seem.
You cannot control how many times the method gets called, since it is not under direct control by you, the programmer.
You can't even control fully if the paintComponent method gets called, since the JVM might decide that too many requests for redraw are being stacked up, and it may not honor all of them.
Instead
read in the data once in a constructor or something similar.
I would create a read method that stores my points in an ArrayList<Point>, and then inside of the paintComponent method, iterate through that ArrayList using a for loop and draw them.
If the points don't change during your program's run, you could even draw them directly on to the BufferedImage by getting its Graphics context and using that to paint the points on to the image, and then show the new BufferedImage in your paintComponent method.
Other suggestions:
That empty catch block where you read your image is a dangerous thing to do. It's the coding equivalent of driving a motorcycle with your eyes closed. At least print out a stacktrace.
The WindowListener is not needed. Instead simply set your JFrame's defaultCloseOperation to JFrame.EXIT_ON_CLOSE: f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Java JComponent.getGraphics() always returns null

I am trying to an object that functions as a button but uses images for display. My problem is that when call getGraphics() it returns null. I have been searching allover the place and cannot find why?
My code for the constructor where it dies at is...
public class ImageButton extends javax.swing.JComponent implements java.awt.event.MouseListener {
private static BufferedImage DEFAULTBUTTON;
private BufferedImage button;
private Graphics g;
public ImageButton(){
//Call the constructor for JComponent
super();
//Grab Graphics
g = this.getGraphics();
//Find the default images
try{
InputStream image;
image = this.getClass().getClassLoader().getResourceAsStream("DefaultButton.png");
DEFAULTBUTTON = ImageIO.read(image);
System.out.println("Default image FINE");
}catch(IOException e){
System.out.println("Default image fail");
}
button = DEFAULTBUTTON;
//Add listener for things like mouse_down, Mouse_up, and Clicked
this.addMouseListener(this);
//Draw the Default button
g.drawImage(button, 0, 0, this);
}
I would LOVE it you could give me help or point it the right direction.
You shouldn't call getGraphics() on a component. Instead, you should override the paintComponent(Graphics) method, and do the painting in this method, using the Graphics object passed as argument.
getGraphics will return null in the constructor as the component will not be visible at the time of creation. For custom painting in Swing override the paintComponent(g) method instead. There the Graphics handle will always be properly initialized.
Here is an example
For more read Performing Custom Painting

Java Applet Buffering images

OK so here's my code: http://www.so.pastebin.com/Qca4ERmy
I am trying to use buffers so the applet won't flicker upon redraw() but it seems I am having trouble. The applet still flickers....
Help?
Thank you.
I made a quick video about this problem: http://www.vimeo.com/12035196
Create a Swing applet. Swing is double buffered by default so you should not have this problem. Start with the section from the Swing tutorial on How to Make Applets for the proper way to create a Swing applet.
The best way I've done it is to create another image the same size as your applet, draw to that, then in your paint / update method copy the contents of that image to your graphics object. You have to make sure that you aren't updating the other image when you draw to your applet otherwise it will cause flicker. Drawing should probably be done in another Thread as well, just to make things a little easier to understand.
I don't have access to my code so the following might be a little off (and the code may not be the most efficient):
public class MyApplet extends Applet {
Image offscreen;
boolean pageFlipped = false;
Thread drawingThread;
public void init() {
offscreen = createImage(this.getWidth(), this.getHeight());
drawingThread = new Thread(new DrawingLoop());
drawingThread.start();
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
if (!pageFlipped) {
g.drawImage(offscreen, 0, 0);
pageFlipped = true;
}
}
class DrawingLoop implements Runnable {
public void run() {
while (true) {
Graphics g = offscreen.getGraphics();
if (pageFlipped) {
// do your graphics code here
pageFlipped = false;
}
}
}
}
}
Hope this helps!
-Dan
You can try to solve this issue using a BufferedImage, in this way you just create a BufferedImage that is compatible with your frame and then draw everything there before blitting the whole image onto the JFrame's content.
A better approach is to use automatic buffering with BufferStrategy class, you can read a tutorial about it here.

Categories

Resources