I am trying to prevent redrawing an animation by the EDT. The first thing i have done is excluding the actual drawing tasks into a different thread, writing into a VolatileImage, which gets redrawn by EDT within paintComponent method of my corresponding JPanel.
If i exclude the repaint into another thread, this works properly. Nevertheless, i do have positioned a couple of other panels above my animation. In result, having called the repaint method of my painter (animation) panel, has caused the others to also get redrawn without flickering.
Therefore to redraw the other panels, calling repaint on painter, results in flickering. Repaint of a single panel results in an opaque redraw with rarely flickering.
Does somebody know, how to synchronize an own repaint of a jpanel, for instance into my already available bufferimage. Id say the repaint triggered to EDT results in flickering, since its not synchronized.
My repaint call to animation
@Override
public void KeyframeChanged(Keyframe frame) {
if (painter.isVisible()) {
map.getMainMap().doPaintComponent(painter.getBuffer().getGraphics());
painter.renderAnimation();
painter.updateScreen();
}
}
painter methods:
public void updateScreen() {
Graphics g = this.getGraphics();
if (g != null) // component already visible?
{
// is there a backBuffer to draw?
if (backBuffer != null) {
g.drawImage(backBuffer, 0, 0, null);
} else {
// if not, create one and render on it
createBackBuffer();
renderAnimation();
}
}
}
public void renderAnimation() {
// Do drawing stuff here
}
@Override
protected void paintComponent(Graphics g) {
// super.paintComponent(g);
}// end of paint
Thanks
Thanks for answers and links. I still need to read a few of them. Nevertheless in order to illustrate the current behavior, this small SSCCE shall help.
package repaintexample;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.VolatileImage;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
class Painter extends JPanel {
private VolatileImage backBuffer;
private Graphics2D g2d;
public Painter() {
setDoubleBuffered(false);
setOpaque(false);
}
@Override
public boolean isOptimizedDrawingEnabled() {
return false;
}
private void createBackBuffer() {
backBuffer = this.getGraphicsConfiguration().createCompatibleVolatileImage(1920, 1200);
}
public void adjustBackBufferSize() {
if (backBuffer != null) {
if (getWidth() > backBuffer.getWidth() || getHeight() > backBuffer.getHeight()) {
createBackBuffer();
}
}
}
public void updateScreen(Graphics g) {
if (g != null) // component already visible?
{
// is there a backBuffer to draw?
if (backBuffer != null) {
g.drawImage(backBuffer, 0, 0, null);
} else {
// if not, create one and render on it
createBackBuffer();
}
}
}
public void renderAnimation(int i, int j) {
if (backBuffer == null) {
createBackBuffer();
}
do {
if (backBuffer.validate(getGraphicsConfiguration()) == VolatileImage.IMAGE_INCOMPATIBLE) {
createBackBuffer();
}
g2d = (Graphics2D) backBuffer.getGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.white);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.setColor(Color.red);
g2d.fillOval(i, j, 50, 50);
} while (backBuffer.contentsLost());
}
@Override
protected void paintComponent(Graphics g) {
// super.paintComponent(g);
updateScreen(g);
}// end of paint
public VolatileImage getBuffer() {
return backBuffer;
}
}
class ContainerFrame extends JFrame {
private Painter mapPainter;
private JPanel informationPanel; // covers a lot of metainformation - Actually own JTable instance updating the same objects for repainting in each circle
private JPanel controller; // Maptools
private JPanel tabbedPabe; // Change redraw content
public ContainerFrame() {
this.setSize(1600, 1024);
this.setVisible(true);
initComponents();
initPositions();
Thread animation = new Thread(new Runnable() {
@Override
public void run() {
// My application is a mapping application, in which i first draw the tiles, before goin on with the "real" animated stuff
// clearing backbuffer content with g.fillRect(0, 0, getWidth(), getHeight());
while (true) {
for (int i = 0; i < mapPainter.getWidth(); i += 100) {
for (int j = 0; j < mapPainter.getHeight(); j += 5) {
mapPainter.renderAnimation(i, j);
int repaintCase = 2;
switch (repaintCase) {
case 0:
// Default case redrawing via EDT, triggering the others in proper order
mapPainter.repaint();
break;
case 1:
// case repainting by current Thread - necessity of repainting above positioned panels
// results in flickering, since not synchronized
mapPainter.updateScreen(mapPainter.getGraphics());
informationPanel.repaint();
controller.repaint();
tabbedPabe.repaint();
break;
case 2:
// painting components on buffer
// Results in rarely flickering and opague repaint
// is there any common way, to manually repaint onto anything - like image
informationPanel.paintAll(mapPainter.getBuffer().getGraphics());
controller.paintAll(mapPainter.getBuffer().getGraphics());
tabbedPabe.paintAll(mapPainter.getBuffer().getGraphics());
mapPainter.updateScreen(mapPainter.getGraphics());
break;
}
}
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
animation.start();
}
private void initComponents() {
mapPainter = new Painter();
mapPainter.setSize(this.getSize());
informationPanel = new JPanel();
informationPanel.setSize(new Dimension(360, 800));
controller = new JPanel();
controller.setSize(new Dimension(500, 250));
tabbedPabe = new JPanel();
tabbedPabe.setSize(new Dimension(300, 300));
this.getLayeredPane().add(mapPainter, JLayeredPane.DEFAULT_LAYER);
this.getLayeredPane().add(controller, JLayeredPane.MODAL_LAYER);
this.getLayeredPane().add(tabbedPabe, JLayeredPane.MODAL_LAYER);
this.getLayeredPane().add(informationPanel, JLayeredPane.MODAL_LAYER);
}
private void initPositions() {
controller.setLocation(mapPainter.getWidth() - controller.getWidth(), mapPainter.getHeight() - controller.getHeight());
tabbedPabe.setLocation(this.getWidth() - tabbedPabe.getWidth(), mapPainter.getHeight() - controller.getHeight() - tabbedPabe.getHeight() - 400);
informationPanel.setLocation(10, mapPainter.getHeight() - informationPanel.getHeight() - 200);
}
}
public class RepaintExample {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
ContainerFrame f = new ContainerFrame();
}
}
I do use case 0 at the moment and do see having either great or pretty bad fps - either 30 or around 6. I am not certain, how that may be possible and i may be able to find sth. in the already posted links. I thought making sure to relieve the EDT at best, could become a proper solution.
Additionally the content of the 3 panels, i illustrated do not need a repaint in the same frequency as the animation does. Unfortunately i haven't found a proper way to prevent the repaint. The only way i have used for quite a while was a paintimmediately in an invokelater call for those areas, which are known as "animated". A common repaint(Rectangle rec) has not been working, since single calls have been summarized to a big one, covering more pixels, than i have passed in.
public void drawCachedSprite(Graphics2D g, CachedSprites sprites, int zoom, double cog, double x, double y, double w, double h) {
try{
pos_x = x;
pos_y = y;
RenderingUtil.getRenderQuality();
transform.setToIdentity();
// Compute the corner, the drawing needs to start with
transform.translate(x - (w / 2.0), y - (h / 2.0));
g.drawImage(sprites.getSprite(DefaultResources.getType(), spriteColor, zoom, cog), transform, null);
} catch (IllegalArgumentException e) {
e.printStackTrace();
System.out.println("width or height not set properly");
}
}