animation pipeline using swing - java

I try to optimize the drawing of my application. Currently i have implemented an animation as well as several GUI components. Some are separated exactly, some overlap each other. Currently i face a problem within the overlapping of swing components. A part of the GUI, which overlaps my animation, needs to draw a lot of Strings and - into an Jlist placed - common swing components.
In result the overlapping GUI becomes redrawn as often as the animation gets an update. I tried using quite a lot of different methods to make sure, whats drawn in front of each other. Things like GlassPane, Jlayeredpane. Unfortunately in any of these tries, the overlapping Menus paintcomponent method, which only need to become called as the user interacts with them, gets called frequently due to animation and causes a quite high cpu usage.
Ive tried to position the Menus in a lower order within the Layeredpane, i.e.:
getLayeredPane().add(map, JLayeredPane.DEFAULT_LAYER);
getLayeredPane().add(mapController, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(settings, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(painter, JLayeredPane.POPUP_LAYER);
During the paint process of the painter i tried to modify the area - i.e.:
#Override
protected void paintComponent(Graphics g) {
g2 = (Graphics2D)g;
g2.setRenderingHints(DefaultResources.getRenderQuality());
g2.clip(JMain.getInstance().getMapBounds());
...}
Well - as soon as the painter component !isOpague(); All components underneath become redrawn. Unfortunately if i do place the menus in a higher order, they as well need to become redrawn with any animation update.
Does somebody has any idea, how to avoid a permanent redraw of overlapping components with an animated component?
The only solution i have seen was using heavyweight containers. Unfortunately the relative positioning has also shown a behavior during moving purposes, which aren't appropriate.
Thanks for any kind of advice!!

Well, its pretty obvious that if you have overlapping non-opaque components all of them will be repainted on any changes in one of them unless you optimize your animation repaint calls to some specific rectangles, so there won't be any useless operations.
Let me describe how Swing works a bit more - all of the paintings you do in paint, paintComponent and other methods (which get called on each component repaint) are done onto subimages of a single image that holds a "cached" version of the whole frame interface.
Now imagine that you change something in your UI (add/remove/repaint component) - that final image (or atleast a small part of it that contains your component) must be properly updated. To do that in case your component is NOT opaque - all of sub-components will be repainted with your component bounds as a repaint rect to create a proper background for your component. If your component is opaque - it will be the only one repainted, but it also has to fill the whole bounds rect on its own, otherwise you will see awful painting artifacts behind your component on each repaint.
To summ up - to avoid pointless repainting of overlapping components there are a few approaches:
Optimize animation repaint calls to areas you actually need to repaint
Use opaque components, unless you want to paint something transparent in your component
Optimize all overlapping components painting operations so repaints won't take much time
There still might be more optimization approaches depending on your specific case, but you will have to find them on your own, since that is impossible without seeing the whole picture.
You can also find a lot of useful information about optimizations in Swing in this book: Filthy Rich Clients

Well in terms of optimization i do have a component, which is causing quite some trouble, but that one i have planned to rewrite. Therefore i just want to make sure, that painting areas are properly. Following that point, i have computed all necessary areas and filled them into a list, which i pass as data has changed.
Its working properly as long as i only have one rectangle applied. As soon as i pass a second one, it seems its y - extension becomes ignored. For instance:
[x=0,y=0,width=322,height=20]
[x=0,y=620,width=322,height=20]
There everything between y=20 and y=620 also gets redrawn.
for (Rectangle rec : clippingAreas) {
painter.repaint(rec);
}
Ok i have tried paintImmediately within the EDT, which does work for now, but i wonder if this is a proper way to go:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (Rectangle rec : clippingAreas) {
painter.paintImmediately(rec);
}
}
});

Ok there we go:
package animationissue;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class AnimationIssue extends JFrame {
JPanel a = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("map has become repainted");
}
};
JPanel b = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("menu as well");
}
};
public AnimationIssue() {
this.setSize(500, 500);
this.setLayout(null);
a.setSize(400, 400);
b.setSize(400, 200);
this.getLayeredPane().add(a, JLayeredPane.DEFAULT_LAYER); // Map
this.getLayeredPane().add(b, JLayeredPane.PALETTE_LAYER); // Menu
a.setLocation(0, 0);
b.setLocation(0, 100);
a.setBackground(Color.red);
b.setBackground(Color.blue);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
// doin some computations for animation
// cast a repaint after having finished new
//animation information i.e. interpolation
while (true) {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
// case 1 - just a repaint of the whole component - triggering map redraw results in menu redraw
// a.repaint();
// case 2 - repaint of specified rectangle
// Either passing one - the menu does not get repainted, or passing both - menu also gets repainted
//a.repaint(0, 0, 400, 100);
//a.repaint(0, 300, 400, 100);
// paintimmediately works for now
//a.paintImmediately(0, 0, 400, 100);
//a.paintImmediately(0, 300, 400, 100);
// Just repainting Menu does not trigger map to become repainted, except if its opague, but then it should become repainted
b.repaint();
}
}
});
t.start();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
AnimationIssue f = new AnimationIssue();
f.setVisible(true);
}
}
I was really looking forward to optimize the behavior, that no menu gets redrawn, if its not necessary. You have to imagine the menu of being a component holding several JLists with a lot of String drawing tasks, which really have a massive impact on cpu usage. I havent been wondering, since it gets redrawn approximately 25 times per sec.
I am just uncertain for the current solution, if using paintImmediately is proper. Apart from this - if you or somebody has an alternative - better - way to prevent useless redrawing (i really thought Glasspane or JLayeredPane or isOptimizedDrawing or isOpaque might help out), i am really thankful.
Best regards.

Related

Why does swing draw simple component twice?

Here is simple example of drawing an oval.
public class SwingPainter extends JFrame{
public SwingPainter() {
super("Swing Painter");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getContentPane().add(new MySwingComponent());
setSize(200, 200);
setVisible(true);
}
public static void main(String[] args) {
new SwingPainter();
}
class MySwingComponent extends JComponent {
public void paintComponent(Graphics g) {
System.out.println("paintComponent");
super.paintComponent(g);
g.setColor(Color.red);
g.fillOval(10, 10, 50, 50);
}
#Override
protected void paintBorder(Graphics g) {
System.out.println("Paint border");
super.paintBorder(g);
}
#Override
protected void paintChildren(Graphics g) {
System.out.println("Paint children");
super.paintChildren(g);
}
}
}
But in debug mode or adding some info to console before drawing (as in example), you can see that swing draws components twice.
paintComponent
Paint border
Paint children
paintComponent
Paint border
Paint children
I cannot understand why it happens, but I think it can affect performance in a difficult GUI.
The article Painting in AWT and Swing: Additional Paint Properties: Opacity suggests why: "The opaque property allows Swing's paint system to detect whether a repaint request on a particular component will require the additional repainting of underlying ancestors or not." Because you extend JComponent, the opaque property is false by default, and optimization is not possible. Set the property true to see the difference, as well as the artifact from not honoring the property. Related examples may be found here and here.
I agree with Konstantin, what you need to remember, the repaint manager is responding to any number of requests when the application starts, this typically includes the initial paint request when the window is shown and resized (there's two).
Try this. Wait till the application is running and resize the window. I'm sure you'll get more then a couple of repaint requests ;)
This works fine to me. In fact, even in debug mode the output was:
paintComponent
Paint border
Paint children
Please, bear in mind that in AWT and Swing components there are many methods (paint, paintBorder, paintChildren, paintComponent, repaint, and others) that are called via call-back, whenever the GUI engine finds suitable. That may vary from JVM to JVM or even from different execution sessions. They can also be triggered from the interaction to your program (if you minimize/maximize, for example). Or they may not, at all.

Java paint() Only Called Once From paintAll()

I have the following code being called:
while(true){
view.onTick();
trySleep(55);
}
The onTick() method is described as such:
public void onTick() {
mainFrame.paintAll(mainFrame.getGraphics());
}
Here is where I set up my JFrame and JPanels etc (mainFrame is a JFrame):
private void runProgramSetup(){
JPanel canvas = new JPanel();
canvas.setLayout(new BoxLayout(canvas, BoxLayout.Y_AXIS));
mainFrame.getContentPane().add(canvas);
//create the main game panel
mapPanel = new MapPanel(model, this);
mapPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MAP_PANEL_HEIGHT));
mapPanel.setBackground(Color.cyan);
//create the menu panel
menuPanel = new MenuPanel(model, this);
menuPanel.setLayout(new BoxLayout(menuPanel, 0));
menuPanel.setPreferredSize(new Dimension(TOTAL_FRAME_WIDTH, MENUS_PANEL_HEIGHT));
//add the panels to the window
canvas.add(mapPanel);
canvas.add(menuPanel);
//make both panels visible
mapPanel.setVisible(true);
menuPanel.setVisible(true);
}
Now here is my problem. Everything repaints when repaintAll() is called EXCEPT mapPanel's overridden paint(Graphics g) method:
#Override
public void paint(Graphics g) {
transformedImages.transformAndStoreImages(model);
paintGrid(g);
paintScenery(g);
paintElements(g);
paintDraggedElement(g);
paintUIOverlay(g);
}
It is only called once. That is it. However, every other component continues to repaint. It is only mapPanel that paints once. Here is what is even more strange. I am running on Ubuntu and get this problem. The rest of my team is running on Macs and they do not have this problem. The only way I have been able to solve this is to replace onTick() with two paint calls:
public void onTick() {
mainFrame.repaint();
mainFrame.paintAll(mainFrame.getGraphics());
}
This is all that has worked for me. I need both calls. Neither works alone. I don't like doing this though obviously because of inefficiency.. :/
Any ideas?
Thanks!
you should be overriding JPanel's
paintComponent(Graphics g)
not paint
The reason mainFrame.repaint() forces the map to refresh is because repaint() calls repaint(0, 0, 0, width, height), which marks the entire mainFrame's area to be marked as "dirty" for the RepaintManager. This is designed this way on purpose, because you usually do not want to repaint every pixel in the JFrame just to update a component. So, in onTick(), when mainFrame.paintAll() is being called, my guess is that the mapPanel's area has not been marked dirty, so the RepaintManager skips it, to save processing time. If you are very sure that you want to repaint the whole mapPanel every time onTick() is called, the simplest way would be to call mapPanel.repaint() inside of your onTick() method. This will mark the whole mapPanel as dirty so it will be redrawn asap. Also, if your menuPanel is just using regular swing JComponents, you don't need to manually cause them to repaint, they will be repainted when their values change, if you are using the API correctly.
I know this is a kind of old question, but I figured I'd answer in case anyone else runs into something similar.

Best way to animate in Java?

I'm currently using an animation engine I designed that takes objects of type Drawable and adds them to a List. Drawable is an interface that has one method:
public void draw(Graphics2D g2d);
The extending animation manager iterates through this list and calls the draw() method on every object, passing the Graphics2D object obtained from the Swing component.
This method seemed to work well at first, but as I feared, it seems to be unable to handle multiple objects in the long run.
With merely two Drawables registered, both drawing images on screen, I'm seeing a bit of flashing after 30-60 seconds.
Is there a way to optimize this method? It currently calls upon the AWT thread (invokeLater) to handle all of the tasks. Concurrent drawing isn't really an option as this nearly always causes issues in Swing/AWT, in large part due to the fact that Graphics isn't synchronized.
If this just simply is a bad way of animating, what is a better method when you have multiple objects that all need things rendered with their own specific variables cough game cough?
Any help would be appreciated. Thanks!
EDIT:
I can't use repaint() beacuse my engine already calls the AWT thread to paint stuff. If I call invokeLater from the AWT thread, the image never gets painted for some reason.
I should also add that I'm using a system of ticks and fps. 60 ticks # 120 fps.
Each tick updates the game logic, while each frame render calls draw on the frame manager.
Is this a bad idea? Should I just use FPS and not ticks?
I think it would be more appropriate to override paintComponent(Graphics g) and regularly call the repaint method on the JPanel or whatever you're drawing on with a Timer. Your problems may be due to to you trying to draw and then Swing doing it's own draw.
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel() {
public void paintComponent(Graphics g) {
//draw here
}
};
panel.setPreferredSize(800, 600);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true)
new Timer(16, new ActionListener() {
public void actionPerformed(ActionEvent event) {
panel.repaint();
}
}).start();
}
}

Invisible JFrame/JTable how much faster?

I have a swing app. with a jframe with lots of internal frames containing large JTable.
Those jtables get updated continuously so there is lots of repainting going on.
in some circumstances I can simply keep the JFrame invisible. (frame.setVisible(false))
I was wondering if anybody knows if I will gain something in terms of performance
(something considerable or not)
such as 50% gain or you would only get 2% gain...
and maybe some sort of explaination on what to expect.
thanks
p.s.
Another way to rephrase the question is:
Are swing components clever enough not to repaint/reflow theirselves if not visible ???
Take a look at the Swing Painting Guidelines which have some useful tips on painting efficiency. For example:
On components with complex output,
repaint() should be invoked with
arguments which define only the
rectangle that needs updating, rather
than the no-arg version, which causes
the entire component to be repainted.
Components which render complex output
should make smart use of the clip
rectangle to narrow the drawing
operations to those which intersect
with the clip area.
It's also quite easy to prove that non-visible components are not painted. The following code hides a panel and prints out if paint is called.
public static void main(String args[]) throws Exception {
JFrame f = new JFrame();
final JPanel p = new JPanel(){
public void paint(Graphics g){
super.paint(g);
System.out.println("IN PAINT");
g.fillRect(10, 10, 20, 20);
}
};
f.setLayout(new BorderLayout());
f.add(p, BorderLayout.CENTER);
JButton b = new JButton("OK");
b.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("PRESSED");
p.setVisible(!p.isVisible());
}
});
f.add(b, BorderLayout.SOUTH);
f.setSize(100,100);
f.setVisible(true);
}
I believe you should temporarily disconnect the TableModel from the JTable while you are not displaying the JTable.
The biggest problem you face is that the JTable reacts to model change events and continuously repaints itself.
It is also possible that the JTable is intelligent enough to not repaint itself while not visible (but has model changes) but I wouldn't put my money on this.
The best way to solve this question is to test both. If you profile both options and see no gain then you try a different route like sola suggested. Just make sure you are not optimizing the wrong part of your application.
I can't give you any numbers on how much performance gain you will see, this depends on too many factors and is best left up to your own profiling. However, if your component is not visible, then the paintComponent() (or repaint()) method won't be called either as far as I know.
If your JTable is in a JScrollPane it seems it will repaint at each line adition (and call to fireTableRowsInserted of your model). Thus because the scrollpane will update it scollbars and then its contained component (JTable). If your table is not in a scrollpane then it will redraw only the shown lines. Therefore if your lines are not shown you will not trigger a repaint... Stupide. I'm looking for a way to avoid this.

Java optimizing painting a background image for a game

I'm working on recreating "Legend of Zelda a Link to the Past" as part of an assignment.
And I've got the following problem, the game world is a BufferedImage of which I subImage() the part I need for the current location of the player. The game works but uses 80 / 110 percent of the CPU. A profile revealed that the culprit is the drawing of the image.
So I figured I put the background in a separate JPanel from the Player, enemies etc JPanel.
Render them on top off each other (JLayeredPane) and repaint the background panel far less often.
But how do I do this how do I tell swing to draw one panel x times a sec and the other y times? If you have a better way of optimizing let me know.
Here's what I've got at the moment:
public class Main extends JFrame
{
private ZeldaGame game = new ZeldaGame();
private View view = new View(game);
private BackgroundView bckView = new BackgroundView(game);
private Controller ctl = new Controller(game, view, bckView, this);
public Main()
{
setLayout(null);
view.setBounds(0, 0, game.getWidth(), game.getHeight());
bckView.setBounds(0, 0, game.getWidth(), game.getHeight());
JLayeredPane pane = new JLayeredPane();
pane.add(bckView, 1);
pane.add(view, 0);
setLayeredPane(pane);
setSize(game.getWidth(), game.getHeight());
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args)
{
new Main();
}
}
Thank You.
It's not really possible to have different render times because of the way the framebuffer works -- what the result would be is loads of nasty flickering. What you need to do is using Canvas instead of JPanel and JLayeredPane and you can either override the paint() method of the canvas or use bi.getGraphics() and blit the background and the characters on a loop using either of these methods in the correct order. I'd advise using a thin engine like GTGE which will abstract from all the messy details of optimisation. These high level components you're using seriously aren't designed for games, and you shouldn't be using them at all.
Ok I've found the error of my ways. I was using passive rendering while I should have used active rendering. Active rendering basically shuts down the automatic repaints by calling setIgnoreRepaint(true); on the frame and do the loop yourself.
And as an added bonus I don't need to use JPanels.

Categories

Resources