Java paintComponent tell if called again - java

Been Googling for a few days and I can't figure out how to tell if paintComponent is being called while I'm processing paintComponent. The process is long and complicated. If I have a second (or third) request to paint, I want to abort the current paint process and start over with the most recent request.

You can't; calls to paintComponent() are queued, blocking the EDT until completed. Use the approach shown in this AnimationTest to self-time your implementation on a typical target platform. Optimize as required. As a concrete example, this KineticModel illustrates several animation techniques that may apply to your use case.
Addendum: Your update suggests that the "long and complicated" process involves constructing a BufferedImage for later display. To avoid blocking the EDT, do this in the background of a SwingWorker from which you can publish() interim results as they become available. This example simulates building a raster image one line at a time.
This related example constructs a TexturePaint in a similar manner.
For specific guidance, edit your question to include a Minimal, Complete, and Verifiable example that shows your revised approach.

As usual, I think of a solution after asking for one... My solution is to launch a thread that progressively updates a BufferedImage. Then, the paintComponent method is just two lines:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(myImage, 0, 0, null);
}
The image is drawn very quickly. Calling super is required to erase the existing view of the image before redrawing it.
The rest of the solution is creating a thread that updates the myImage attribute of the panel. In my work, the image takes about 30 minutes to process completely, depending on the input to the program. It isn't a big deal unless I drag the window or accidentally touch the mouse wheel while the application is running. Then, paintComponent is called again and hangs the program. With this solution, the update can take days and you can resize or move the window all you want. You keep seeing the image as it has been processed so far.

Related

Seems like the SWT redrawing commands are skipped that leads to appearing artifacts on the screen

I’ve encountered with the following problem related to the Control.redraw(int, int, int, int, boolean) method called on Canvas. I’m working on the diagram editor. Recently I’ve implemented the selection tool. It shows a rectangular frame on the diagram. The problem is that I get artifacts on the screen when I resize quickly this selection tool with help of mouse. But everything is redrawn correctly when I resize the tool slowly.
In the SWT language it looks as follows.
Each time the selection rectangle is resized with help of mouse, I eventually call the redraw method specifying the corresponded rectangle area to redraw. Then I catch the PaintEvent and paint the frame. Everything works as expected if the mouse movement is slow.
The questions is can SWT skip the painting events or skip the redraw commands? Does anybody encountered with a similar issue? Maybe, is there some flag that should be set?
So, I tried to setRedraw(true) but it didn’t give any effect. Finally, I tried to replace the redraw(int, int, int, int, boolean) method with just redraw() and the artifacts disappeared but this is not acceptable solution as it created sensible lags at time of resizing the selection tool, especially on Windows.
Thanks
If you are asking whether every single call to redraw is matched with paint event, then the answer is no. It's possible that OS will decide to merge several redraw requests into one paint event (which, nonetheless, should cover the area of all original requests). Note that paint requests can be triggered using update() method - redraw() will simply invalidate an area and mark it for repaint, but doesn't explicitly trigger the repaint.
It's possible that OS has some problems with repaints, but it is far more likely that the problem is in your painting code/redraw algorithm.

Working my way arround repaint() in Java

I'm planning to write a simple spaceshooter. I have read that the repaint() method is only a request, and it doesn't execute every time it's called. I believe I'm noticing the effects of this, as my spaceship tends to lag ever so slightly when I'm moving it. Currently I'm simply drawing my ship in a a JPanel's paintComponent() method, and keep calling repaint() on regular intervals (my panel's also Runnable). Seeing as repaint() may potentially screw me over, I'm trying to find a way to work arround it, however I've ran out of ideas. The code I have so far:
private void renderGraphics() {
if (MyImage == null) {
MyImage = new BufferedImage(getPreferredSize().width,
getPreferredSize().height, BufferedImage.TYPE_INT_RGB);
}
MyGraphics = MyImage.getGraphics();
MyGraphics.setColor(Color.BLACK);
MyGraphics.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
MyGraphics.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);
}
The idea was to create my own graphics, and then make the JPanel draw it, and keep calling this instead of repaint() in my run() method, however I have no idea how to do that. I'd appriciate any input on the matter.
There are multiple ways to approach this.
The best is probably to use BufferStrategy and draw to that, of which I have included a code snippet that should work for you.
You can take this one step further and abandon Swing altogether, just using Frame/BufferStrategy. There is a fully working example (from which the code snippet was taken and adapted) in my question here:
AWT custom rendering - capture smooth resizes and eliminate resize flicker
Anyway, here is an implementation BufferStrategy that you should be able to just drop in:
// you should be extending JFrame
public void addNotify() {
super.addNotify();
createBufferStrategy(2);
}
private synchronized void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy==null) return;
sizeChanged = false;
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
MyGraphics draw = strategy.getDrawGraphics();
draw.setColor(Color.BLACK);
draw.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
draw.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);
draw.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
any drawing will still be performed in the Swing Thread, so no matter what you try work around, it wont help.
Make sure you are not doing any lengthy calculations in the swing thread, this may be stopping repaint from being executed as soon as it needs to be executed
Separate all the logic into 2 parts. Static and Dynamic. (e.g. sea and moving ship. Ship changes shape/location on a static image of sea)
Draw static content in an image once and use the image in your paintComponent(). Call dynamic parts painting after the static image.
Use setClip() to restrict repainting areas.
Calling repaint without any arguments means that the whole panel is repainted.
If you need to repaint parts of the screen (the spaceship has moved to a different location) you should make shure that only those parts of the screen are repainted. The areas that stay the same should not be touched.
Repaint takes coordinates of a rectangle that should be repainted. When moving the ship you should know the old coordinates of the ship and the coordinates the ship should move to.
repaint( oldShipCoordinateX, oldShipCoordinateY, shipWidth, shipLength );
repaint( newShipCoordinateX, newShipCoordinateY, shipWidth, shipLength );
This is usually much faster than calling repaint() without arguments. However you have extra effort to remember the last position of the ship and must be able to calculate the new position of the ship.
See also: http://download.oracle.com/javase/tutorial/uiswing/painting/index.html - especially step 3
Just for code that you post here:
1/ if you want to display Image/ImageIcon, then the best and easiest way is to Use Labels
2/ as you mentioned Runnable{...}.start(); Swing is simple threaded and all output to GUI must be done on EDT; you have to look at Concurrency in Swing, result is that all output from BackGround Task(s) must be wrapped into invokeLater(), and if is there problem with perfomancie then into invokeAndWait()
3/ if you be switch (between JComponents)/add/delete/change Layout then you have to call revalidate() + repaint() as last lines in concrete code block
EDIT:
dirty hack would be paintImmediately()
I have read that the repaint() method is only a request, and it doesn't execute every time it's called
It consolidates multiple repaint() requests into one to be more efficient.
I believe I'm noticing the effects of this, as my spaceship tends to lag ever so slightly when I'm moving it.
Then post your SSCCE that demonstrates this problem. I suspect the problem is your code.
Regarding the solution you accepted, take a look at Charles last posting: Swing/JFrame vs AWT/Frame for rendering outside the EDT comparing Swing vs AWT solutions.

protected void paintComponent(Graphics g)

We can override the paintComponent(Graphics g) method of JComponent.
It is called automatically to refresh the screen.
What can I do that is called regularly, for example every 100 ms.
Is this possible?
You can request that the component repaint itself with a call to repaint(). You most certainly do not want to call repaint every 1 ms though; no monitor in the world could display at 1000 Hz, and no human eye would distinguish that either. Furthermore the repaint calls would be coalesced into a few separate ones and you would not achieve that number of repaints.
What are you trying to achieve? I assume it's some sort of animation; if so you should start googling for "java 2d swing animation" or some variant of that. Try this 2d java tutorial for starters.
Maybe your purpose is refreshing the component every certain ms?
If so, there's a few options available.The use of Timer is a first option. Example here.
The option most people use is Thread animation. Here is an example.
Also there's a timing framework available to download. I can't find an available link, but just search for it on the internet.
Hope this helps.

Optimizing Java Graphics

I have a custom UI drawn for my java application. Right now I draw the entire UI from scratch. I know for a fact some parts of the UI are static. I know I could save these static parts to an image, but will this yield a improvement in performance (Since even an image must be drawn every frame)?
Is it plausible to save a reference to the Graphics2D object after the UI has been drawn and assign that to the new graphics object every frame (starting from a point where all the static components are drawn)?
Thanks in advance,
Alan
You don't need to redraw everything in every frame. So if you have static parts of your UI (or even dynamic parts that you know haven't changed since last frame) then you simply don't need to repaint them.
In my code (Swing games and simulations mostly) I usually try to follow the following rules:
Pre-prepare static images (e.g. BufferedImage textures for UI elements)
Override the paintComponent() method for each UI element individually to do the painting
Only call the repaint() method of any given UI element if I know that something has changed
Call repaint() in a timer-based loop for animation, but only call it on the portion of the UI that is being animated (e.g. a nested JPanel)
This approach seems to work and perform pretty well (though I'd welcome comments if there are ways to improve it!!)
There are two main optimizations you can do here. The first is to make sure that when you cause your UI to be repainted, usually done by calling repaint, make sure you call the version of repaint where you specify a rectangle that has changed. Only make the rectangle big enough to encompass the parts that actually have changed, not the static parts. For this to be effective you also have to pay attention to the clipRect in the Graphics2D object you are passed in paint(). That is used by the system to tell you exactly what needs to be repainted, in the above case usually the rectangle that you passed to repaint. Don't paint anything that lies entirely outside that rectangle.
You can also get significant performance improvements by caching the static parts of your interface in an image. Writing an image is by far the fastest way of getting things onto a screen. My measurements indicate that small images are faster than even a few simple drawing primitives. However you need to make sure the image characteristics match the screen, by using createCompatibleImage().
Of course you may be using a lot of memory to get this speedup. I would recommend testing to see if you need to do image caching before implementing it.
if some parts of the screen is completely static, then never redraw that part. Don't do a full-screen/window clear, just clear the part of the screen/window that changes all the time.
This way, you don't unnecessarily redraw the static image.

BufferStrategy vs DIY Double Buffering in JFrame

Until now, I've done double buffering by creating and Image, drawing what I wanted to that Image using its associated Graphics object then draw that Image to the screen using the paint method's Graphics object. Recently, I learned about the BufferStrategy class and its uses. I was wondering what are the pros and cons of the two methods.
EDIT:
I dont't think I made my question very clear. I wanted to know the pros/cons of both the DIY method and the BufferStrategy and when, if ever, I should use one or the other.
I've always had good results using the default BufferStrategy by being careful to
Always construct GUI components on the EDT
Never draw from a thread except the EDT
This excellent example must double buffer because it draws continually on the initial thread rather than the EDT. In contrast, this fairly busy example relies on nothing more than repaint() called in response to a Swing Timer. I rarely need an offscreen buffer except for a composite. Finally, this tutorial article offers more guidelines.
I recommend reading Painting in AWT and Swing if you haven't.
I don't think you usually need Do-It-Yourself double buffering if you're using JFrame. Swing has built in double buffering which is turned on by default. Manually doing this yourself will just slow things down. You can check if double buffering is on by calling isDoubleBufferingEnabled() on any of your JComponents.
There are cases where you may want to do this yourself, but that should be the exception rather than the rule. Maybe you're doing something involved like writing a game, in which case maybe my advice doesn't apply. Anyway, hopefully the above is useful info.

Categories