I can't get double buffering to work - java

I have tried quite a few tutorials and I can't seem to get double buffering to work. Here is my main (the update() method is the code i tried, but I still see flickering):
public class Main extends JApplet implements Runnable {
private static final long serialVersionUID = 1L;
private static int width = 900;
private static int height = 600;
public static int fps = 60;
public Thread thread = new Thread(this);
private Image dbImage;
private Graphics dbg;
public static Ailoid ailoid = new Ailoid();
// Initialize
public void init() {
setSize(width, height);
setBackground(Color.white);
ailoid.setLocation(new Location(100, 100));
AlienManager.registerAlien(ailoid);
}
// Paint graphics
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.green);
for (Alien alien : AlienManager.getAliens()) {
Location loc = alien.getLocation();
int x = loc.getX();
int y = loc.getY();
g.fillRect(x, y, 10, 20);
}
}
// Update graphics for double buffering
public void update(Graphics g) {
if (dbImage == null) {
dbImage = createImage (width, height);
dbg = dbImage.getGraphics();
}
dbg.setColor (getBackground ());
dbg.fillRect (0, 0, width, height);
dbg.setColor (getForeground());
paint (dbg);
g.drawImage (dbImage, 0, 0, this);
}
// Thread start
#Override
public void start() {
thread.start();
}
// Thread stop
#Override
public void destroy() {
thread = null;
}
// Thread run
#Override
public void run() {
while (thread != null) {
Updater.run();
repaint();
try {
// 1000 divided by fps to get frames per millisecond
Thread.sleep(1000 / fps);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
If someone could help it would be greatly appreciated!

Instead of drawing your objects one by one, draw them on an image and then tell the renderer to draw that entire image. This eliminates the flickering.
Here's an example of how you might accomplish it.
class DoubleBufferedCanvas extends Canvas {
public void update(Graphics g) {
Graphics offgc;
Image offscreen = null;
Dimension d = size();
// create the offscreen buffer and associated Graphics
offscreen = createImage(d.width, d.height);
offgc = offscreen.getGraphics();
// clear the exposed area
offgc.setColor(getBackground());
offgc.fillRect(0, 0, d.width, d.height);
offgc.setColor(getForeground());
// do normal redraw
paint(offgc);
// transfer offscreen to window
g.drawImage(offscreen, 0, 0, this);
}
}

Related

JSlider image behind track

I want to put an image (visualization of an audio) behind the JSlider which represents the audioplayer, the process of playing.
First I tried to overwrite the paint-method of the Slider
public void paintComponent(Graphics g) {
// Draw the previously loaded image to Component
g.drawImage(img, 0, -100, null);
super.paintComponent(g);
}
this worked, but the image is higher than the slider, so my next try was a JLayeredPane, where I put the JSlider above a JLabel with the image. Looks good for the first moment. But I mentioned that I need the image behind the track of the slider, not the whole slider. There is space to the left and right. Can anybody tell me a way how to calculate this space? Or the width and offset of the track to the border of the slider? This should run under Windows and MacOs, so different LookAndFeels, so I think hard coded values will not work.
Example Slider with background image
Thankyou.
My solution for this Problem is now to overwrite the SliderUI. So this is a very special component, so it's nonrelevant that it looks the same on all LookAndFeels.
It supports also jumping directly to mouse position, which is different to BasicSliderUI.
/**
*
* Custom SliderUI for AudioPlayer with audioimage in background
*/
public class AudioSliderUI extends BasicSliderUI {
private BasicStroke stroke = new BasicStroke(1f, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND, 0f);
public AudioSliderUI(AudioSlider b) {
super(b);
}
#Override
public void paint(Graphics g, JComponent c) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
super.paint(g, c);
}
#Override
protected Dimension getThumbSize() {
return new Dimension(2, 200);
}
#Override
public void paintTrack(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Stroke old = g2d.getStroke();
g2d.setStroke(stroke);
g2d.setPaint(Color.WHITE);
if (slider.getOrientation() == SwingConstants.HORIZONTAL) {
g2d.drawLine(trackRect.x, trackRect.y + trackRect.height / 2,
trackRect.x + trackRect.width, trackRect.y + trackRect.height / 2);
} else {
g2d.drawLine(trackRect.x + trackRect.width / 2, trackRect.y,
trackRect.x + trackRect.width / 2, trackRect.y + trackRect.height);
}
g2d.setStroke(old);
Image img = ((AudioSlider)slider).getImage();
g2d.drawImage(img, trackRect.x, trackRect.y, trackRect.width, trackRect.height, slider);
}
#Override
public void paintThumb(Graphics g) {
Rectangle knobBounds = thumbRect;
int w = knobBounds.width;
int h = 100;
int newStarty = knobBounds.height/2- h/2;
g.translate(knobBounds.x, knobBounds.y);
// "plain" version
g.setColor(Color.YELLOW);
g.fillRect(0, newStarty, w, h);
}
#Override
protected TrackListener createTrackListener(JSlider slider) {
return new TrackListener() {
#Override
public void mousePressed(MouseEvent e) {
if (UIManager.getBoolean("Slider.onlyLeftMouseButtonDrag")
&& SwingUtilities.isLeftMouseButton(e)) {
JSlider slider = (JSlider) e.getComponent();
switch (slider.getOrientation()) {
case SwingConstants.VERTICAL:
slider.setValue(valueForYPosition(e.getY()));
break;
case SwingConstants.HORIZONTAL:
slider.setValue(valueForXPosition(e.getX()));
break;
default:
throw new IllegalArgumentException(
"orientation must be one of: VERTICAL, HORIZONTAL");
}
super.mousePressed(e); // isDragging = true;
super.mouseDragged(e);
} else {
super.mousePressed(e);
}
}
#Override
public boolean shouldScroll(int direction) {
return false;
}
};
}
}
Matching Slider:
public class AudioSlider extends JSlider {
private Image img;
public AudioSlider() {
setOpaque(false);
}
/**
* #return the img
*/
public Image getImage() {
return img;
}
public void setImage(Image img) {
this.img = img;
}
}
Works for me, maybe covers not all prospects.

Scrolling background, JAVA

I need to make my background in a game move up all the time..
I know that I need to use some thread that added variable to 'y' coordinate of the image
I tried to do something but when it started moving all the background get streaking in some reason, cant understand why...
picture : streaking background
public class Background {
private BufferedImage image;
.....
....
public Background(int x, int y) {
this.x = x;
this.y = y;
// Try to open the image file background.png
try {
BufferedImageLoader loader = new BufferedImageLoader();
image = loader.loadImage("/backSpace.png");
}
catch (Exception e) { System.out.println(e); }
}
/**
* Method that draws the image onto the Graphics object passed
* #param window
*/
public void draw(Graphics window) {
// Draw the image onto the Graphics reference
window.drawImage(image, getX(), getY(), image.getWidth(), image.getHeight(), null);
// Move the x position left for next time
this.y +=1 ;
}
public class ScrollingBackground extends Canvas implements Runnable {
// Two copies of the background image to scroll
private Background backOne;
private Background backTwo;
private BufferedImage back;
public ScrollingBackground() {
backOne = new Background();
backTwo = new Background(backOne.getImageWidth(), 0);
new Thread(this).start();
setVisible(true);
}
#Override
public void run() {
try {
while (true) {
Thread.currentThread().sleep(5);
repaint();
}
}
catch (Exception e) {}
}
#Override
public void update(Graphics window) {
paint(window);
}
public void paint(Graphics window) {
Graphics2D twoD = (Graphics2D)window;
if (back == null)
back = (BufferedImage)(createImage(getWidth(), getHeight()));
Graphics buffer = back.createGraphics();
backOne.draw(buffer);
backTwo.draw(buffer);
twoD.drawImage(back, null, 0, 0);
}
You dont need a new thread for it, that just overcomplicates things in this case.
You just need to add a factor to the Y coordinate of the background continuesly.
For example:
float scrollFactor = 0.5f; //Moves the background 0.5 pixels up per call of the draw method
public void draw(Graphics window) {
window.drawImage(image, getX(), getY(), image.getWidth(), image.getHeight(), null);
this.y += scrollFactor;
}

System Freezes upon Running this code

I'm following a Java game programming series on youtube, and all have been going well until we add some code to the program. The code for the program is:
package com.fagyapong.rain;
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = -247215114548172830L;
public static int width = 300;
public static int height = width / 16 * 9;
public static int scale = 3;
private JFrame frame;
public Thread thread;
private boolean running = false;
public Game() {
// Setup Game window
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
frame = new JFrame();
}
public synchronized void start() {
running = true;
thread = new Thread(this, "Display");
thread.start();
}
public synchronized void stop() {
running = false;
try {
thread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
public void run() {
while (running) {
update();
render();
}
}
public void update() {
}
public void render() {
// Get the canvas' BufferStragy object
BufferStrategy bs = getBufferStrategy();
if (bs == null) {
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.dispose();
bs.show();
}
public static void main(String[] args) {
Game game = new Game();
game.frame.setResizable(false);
game.frame.setTitle("Rain");
game.frame.add(game);
game.frame.pack();
game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.frame.setLocationRelativeTo(null);
game.frame.setVisible(true);
game.start();
}
}
Below is code that causes the system freeze(it has been commented out in the above code)
Graphics g = bs.getDrawGraphics();
g.setColor(Color.GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.dispose();
bs.show();
Okay, I figured out the problem a bit. I was having the same issue. The issue for me was the triple buffering. Instead, set the code to:
createBufferStrategy(2);
That way it's only double buffering. I don't have a fantastic computer, so I had to set it to 1 instead of 2. At that point, my guess is that it's not buffering at all. This is how I got it to work though.

Java Swing - Valid approach for dragging rectangles onto a JPanel?

I have some code to draw rectangles. It's used to draw rectangles on a JPanel, to mark boundaries of widgets. Here the code first, after that I'll explain my problem cq. question.
First off, I have a class (WidgetDrawingPanel) which extends JPanel.
public WidgetDrawingPanel(int width, int height) {
/*To make things visible at least*/
widgets.add(new Widget(10,10,100,100, WidgetType.TextField));
widgets.add(new Widget(50,50,100,200, WidgetType.TextField));
this.width = width;
this.height = height;
this.setBackground(Color.BLUE);
addListener(); //adds both MouseMotionListener and MouseListener
}
Below you'll see me reference ch a lot. This is a CoordinateHolder, which holds start and current coordinates of my mouse movement.
private void addListener() {
this.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent arg0) {
ch.currentX = arg0.getX();
ch.currentY = arg0.getY();
System.out.println("dragging " + ch.currentX + ","+ch.currentY);
WidgetDrawingPanel.this.repaint();
}
});
this.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent event) {
ch.endX = event.getX();
ch.endY = event.getY();
try {
checkCoords();
} catch (OutsidePanelException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "drawn Outside Panel");
}
}
#Override
public void mousePressed(MouseEvent event) {
ch = new CoordinateHolder(event.getX(), event.getY());
}
});
}
and, finally, the paintComponent(Grapics) method. There's loop through Widgets, which are actually just already drawn Rects (x, y, w, h attributes), but which a little more information, which is not useful in the drawing part of the application. Everytime you release the mouse, the CoordinateHolder is converted into a Widget, and added to widgets.
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("Paint");
g.setColor(Color.BLUE);
g.fillRect(0, 0, width, height); //making the whole panel blue
g.setColor(Color.RED);
Graphics2D g2 = (Graphics2D)g;
g2.setStroke(new BasicStroke(3));
for (Widget w : widgets) {
g.drawRect(w.getX(), w.getY(), w.getW(), w.getH());
}
if (ch != null)
g.drawRect(ch.startX, ch.startY, ch.currentX - ch.startX, ch.currentY - ch.startY);
}
This code is working, but I suspect this is highly inefficient and inperformant, as above code continually refreshes the JPanel on mouse drag, which is, say, once every 10ms? I suppose it'll get slow really soon, especially when the user creates a heck of a lot rectangles (which are also continally redrawn, as seen in painComponent(Graphics)).
Question cq. Problem
Is there a better, less resource consuming method, where the user can drag rectangles smoothly?
I read an answer to this Drag rectangle on JFrame in Java, but the author of that answer seems to do it the same as me. But again, that's way inperformant, right? Or should computers be easily able to redraw the component continually, and is this actually a valid approach?
To show lots of non-changing background shapes, draw them to a BufferedImage and then show that BufferedImage in the paintComponent(...) method. So while a shape is being drawn, draw it in paintComponent(...) but once the shape is done being drawn, perhaps on mouseRelease, then draw it in the background BufferedImage.
Note that what will slow your current drawing code the most may be your debugging SOP statements, but I assume that these will be removed from the finished code.
For example:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawingPanel extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 400;
private static final Color DRAWING_COLOR = new Color(255, 100, 200);
private static final Color FINAL_DRAWING_COLOR = Color.red;
private BufferedImage backgroundImg;
private Point startPt = null;
private Point endPt = null;
private Point currentPt = null;
public DrawingPanel() {
backgroundImg = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
Graphics g = backgroundImg.getGraphics();
g.setColor(Color.blue);
g.fillRect(0, 0, PREF_W, PREF_H);
g.dispose();
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseMotionListener(myMouseAdapter);
addMouseListener(myMouseAdapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (backgroundImg != null) {
g.drawImage(backgroundImg, 0, 0, this);
}
if (startPt != null && currentPt != null) {
g.setColor(DRAWING_COLOR);
int x = Math.min(startPt.x, currentPt.x);
int y = Math.min(startPt.y, currentPt.y);
int width = Math.abs(startPt.x - currentPt.x);
int height = Math.abs(startPt.y - currentPt.y);
g.drawRect(x, y, width, height);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void drawToBackground() {
Graphics g = backgroundImg.getGraphics();
g.setColor(FINAL_DRAWING_COLOR);
int x = Math.min(startPt.x, endPt.x);
int y = Math.min(startPt.y, endPt.y);
int width = Math.abs(startPt.x - endPt.x);
int height = Math.abs(startPt.y - endPt.y);
g.drawRect(x, y, width, height);
g.dispose();
startPt = null;
repaint();
}
private class MyMouseAdapter extends MouseAdapter {
#Override
public void mouseDragged(MouseEvent mEvt) {
currentPt = mEvt.getPoint();
DrawingPanel.this.repaint();
}
#Override
public void mouseReleased(MouseEvent mEvt) {
endPt = mEvt.getPoint();
currentPt = null;
drawToBackground();
}
#Override
public void mousePressed(MouseEvent mEvt) {
startPt = mEvt.getPoint();
}
}
private static void createAndShowGui() {
DrawingPanel mainPanel = new DrawingPanel();
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

Java applet only partially paints

I'm new to Java applet programming, so excuse me if this is a very basic question, but I've googled it extensively and have only found semi-related problems and solutions.
I'm writing a simple demonstration of some geometric algorithms, and when I repaint(), only some of my graphics primitives are rendered to the screen. Every time my applet redraws, a seemingly random subset of my lines and ellipses are painted. The only pattern to it is that the primitives that are rendered are always from the beginning of the drawing. I.E, sometimes it will draw primitives 0-2, sometimes 0-5, sometimes the whole batch.
I would like to point out that, as far as I can tell, this is not the classic "flickering" that can be solved with double-bufferring. To my understanding, flickering is when for short periods of time you can see a partially rendered applet before it finishes rendering. In my case, however, if it doesn't finish rendering, it never finishes unless I redraw() again and get lucky. I've tried double buffering:
public void update(Graphics g) {
Graphics offgc;
Image offscreen = null;
Dimension d = size();
// create the offscreen buffer and associated Graphics
offscreen = createImage(d.width, d.height);
offgc = offscreen.getGraphics();
// clear the exposed area
offgc.setColor(getBackground());
offgc.fillRect(0, 0, d.width, d.height);
offgc.setColor(getForeground());
// do normal redraw
paint(offgc);
// transfer offscreen to window
g.drawImage(offscreen, 0, 0, this);
}
But it doesn't seem to help at all. If it's useful, here's some pics of what's happening. This is what it's supposed to look like:
But most of the time it looks something like this:
or this:
Thanks in advance!
This isn't really how double buffering should work, no is it how the paint process works.
Don't override update.
Don't override paint of top level containers (like Applet/JApplet/Frame/JFrame) where possible
Use a "paint" panel onto which you can render, preferably something like JPanel. Swing components provide double buffering support
A double buffer should be painted on out side the paint cycle and only updated when needed, this makes the overall paint process faster as you're not having re-render the content unnecessarily.
When it comes time to update the buffer, render to a temp buffer first, this ensures that any repaints that might occur while you're updating aren't reflected back to the screen prematurely...
public class TestPaintGeometry {
public static void main(String[] args) {
new TestPaintGeometry();
}
public TestPaintGeometry() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new ShowPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ShowPane extends JPanel {
private GeometryPane geoPane;
public ShowPane() {
setLayout(new BorderLayout());
geoPane = new GeometryPane();
JButton redrew = new JButton("Redraw");
add(geoPane);
add(redrew, BorderLayout.SOUTH);
redrew.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
geoPane.redrew();
}
});
}
}
public class GeometryPane extends JPanel {
private BufferedImage buffer;
public void redrew() {
Path2D.Float path = new Path2D.Float();
int width = getWidth();
int height = getHeight();
int points = Math.max(10, (int) Math.round(Math.random() * 100));
for (int index = 0; index < points; index++) {
int x = (int) Math.round(Math.random() * width);
int y = (int) Math.round(Math.random() * height);
if (index > 0) {
path.lineTo(x, y);
} else {
path.moveTo(x, y);
}
}
BufferedImage tmp = createCompatibleImage(width, height);
Graphics2D g2d = tmp.createGraphics();
g2d.setColor(Color.BLACK);
g2d.draw(path);
g2d.dispose();
buffer = tmp;
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (buffer != null) {
int x = (getWidth() - buffer.getWidth()) / 2;
int y = (getHeight() - buffer.getHeight()) / 2;
g.drawImage(buffer, x, y, this);
}
}
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
public static BufferedImage createCompatibleImage(int width, int height) {
return createCompatibleImage(width, height, Transparency.TRANSLUCENT);
}
public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;
}
}
This allows you deploy the GeometryPane to a JFrame or JAppelt as it's not constrained by the legacy of it's inheritance...

Categories