I am trying to make an animation by Java but having trouble understanding the DoubleBufferImage and DoubleBufferGraphics. I understand about the update(), render(), and paint() sequences. However, In the methods of each, I can't understand how it is being drawn. Here are the codes.
gameUpdate()
// I will just skip the updating part because I first need to understand the background.
gameRender()
private void gameRender() {
if (DoubleBufferImage == null) {
System.out.println("The Image is null: Error occurence");
DoubleBufferImage = createImage(P_WIDTH - 15, P_HEIGHT - 15);
}
DoubleBufferGraphic = DoubleBufferImage.getGraphics();
DoubleBufferGraphic.setColor(Color.LIGHT_GRAY);
DoubleBufferGraphic.fillRect(0, 0, P_WIDTH, P_HEIGHT);
DoubleBufferGraphic.setColor(Color.BLUE);
DoubleBufferGraphic.setFont(font);
DoubleBufferGraphic.drawString("Average FPS/UPS: " +
df.format(averageFPS) + df.format(averageUPS), 10, 10);
}
paint()
public void paintScreen() {
Graphics g;
try {
g = this.getGraphics();
if ((g != null) && (DoubleBufferImage != null)) {
g.drawImage(DoubleBufferImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync();
} catch (Exception e) {
System.out.println("Graphics context error: " + e);
}
}
There's also paintComponent method which is overrided from Jpanel.
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (DoubleBufferImage != null) {
g.drawImage(DoubleBufferImage, 0, 0, null);
}
}
Here's my question. The running goes as update -> render -> and painting
However as you can see from the render, they used graphics to draw. But is it all drawn in DoubleBufferImage? Is Image instance similar to panel instance? I mean, is it just addable to the frame?
As this is the Double Buffering system, I want to know which methods is drawing directly, and which one is the method that draws beforehand.
Finally in the running, there is no code that it's the Image or Graphics that we made are going to be added to the panel. I just want to know the timing that the BufferedImage is being drawn.
Please help! I can't upload the whole code so I'm not sure if u guys can understand :(
Okay, so you seem to be banging against some misinformation and misunderstandings. Perhaps you should start by having a look at something like paintComponent() vs paint() and JPanel vs Canvas in a paintbrush-type GUI
In AWT/Swing there are at least two ways of performing custom painting, each with there pros and cons.
You could use paintComponent, which a "hook" into the painting system used by Swing. Swing makes use of a "passive" rendering system. This means you don't have control of when something get's painted, the paint system makes those decisions and then calls (indirectly) the paintComponent of your component so you can perform the updates.
The other mechanism (BufferStrategy) uses a "active" rendering system, which gives you complete control over when painting occurs.
As a general rule, you can't mix them. Swing has it's own painting system and won't play well with BufferStrategy, so that means if you want to use Swing components as part of your output, you can't.
But that won't answer your question, or not directly
Let's try and break it down
gameRender
private void gameRender() {
if (DoubleBufferImage == null) {
System.out.println("The Image is null: Error occurence");
DoubleBufferImage = createImage(P_WIDTH - 15, P_HEIGHT - 15);
}
DoubleBufferGraphic = DoubleBufferImage.getGraphics();
DoubleBufferGraphic.setColor(Color.LIGHT_GRAY);
DoubleBufferGraphic.fillRect(0, 0, P_WIDTH, P_HEIGHT);
DoubleBufferGraphic.setColor(Color.BLUE);
DoubleBufferGraphic.setFont(font);
DoubleBufferGraphic.drawString("Average FPS/UPS: " +
df.format(averageFPS) + df.format(averageUPS), 10, 10);
}
At this moment in time, DoubleBufferImage seems to be a BufferedImage, so when gameRender is called, it checks to see if a buffer exists and creates it as needed. It then takes a reference of the DoubleBufferImages Graphics context and prepares it for rendering, cleaning off what was previously painted to it.
A quick overview of Graphics
Graphics is an abstract layer over the top of the underlying rendering pipeline, often implement using either OpenGL or DirectX depending on the system. It provides a common layer onto which graphics operations can be performed in a system independent manner
paintScreen
public void paintScreen() {
Graphics g;
try {
g = this.getGraphics();
if ((g != null) && (DoubleBufferImage != null)) {
g.drawImage(DoubleBufferImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync();
} catch (Exception e) {
System.out.println("Graphics context error: " + e);
}
}
This worries me, as I have no context, but this.getGraphics() seems to be taking a reference of a component's Graphics context and drawing the DoubleBufferImage to it.
This is dangerous and ill advised. getGraphics returns a snapshot of the component from when it was last painted, which could be painted over at anytime when the component is painted again.
You should, at the earliest opportunity, get rid of this method.
paintComponent
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (DoubleBufferImage != null) {
g.drawImage(DoubleBufferImage, 0, 0, null);
}
}
As mentioned above, paintComponent is the preferred method for hooking into the paint system of Swing. All this does is paints the DoubleBufferImage it, although it should actually read g.drawImage(DoubleBufferImage, 0, 0, this);
So, what is all this doing?
Basically, it's an ill-advised attempt to perform double buffering in Swing ... which is already double buffered by default.
However as you can see from the render, they used graphics to draw. But is it all drawn in DoubleBufferImage? Is Image instance similar to panel instance?
No. A component is an object, which has a number of properties. One of it's jobs is to paint it's state, which is done via the various paint methods, which is passed a Graphics context which is attached to a native peer and eventually will be rendered to the screen (or printer).
A BufferedImage is just that, an image. The Graphics context is just a simple means by which you can paint to it. It can then be saved to a file or, as is the case here, painted to a component (via it's Graphics) context.
As I said above, Graphics is just an abstract layer, which allows you to perform painting operations to a number of different destinations, screen, printers, images, etc...
I mean, is it just addable to the frame?
No. It's not a component based class
As this is the Double Buffering system, I want to know which methods is drawing directly, and which one is the method that draws beforehand.
paintComponent and (the ill-advised) paintScreen are painting the image directly to the component, which will, eventually be rendered to the screen by the painting sub system.
Further reading...
Performing Custom Painting
Painting in AWT and Swing
Passive vs. Active Rendering
BufferStrategy and BufferCapabilities
Related
I wanted to use double buffering for my code and I found this in the internet. But I dont understand how it works.
private Image doubleBuffer;
public void update(Graphics g) {
Dimension size = getSize();
if (doubleBuffer == null || doubleBuffer.getWidth(this) != size.width || doubleBuffer.getHeight(this) != size.height){
doubleBuffer = createImage(size.width, size.height);
}
if (doubleBuffer != null) {
Graphics g2 = doubleBuffer.getGraphics();
paint(g2);
g2.dispose();
g.drawImage(doubleBuffer, 0, 0, null);
}
else {
paint(g);
}
The double buffer technique works in such way that you render/draw all graphics to an offscreen buffer (offscreen image), often called a backbuffer. Once all rendering/drawing is complete, the backbuffer is drawn/copied to the screen buffer.
The code starts with getting the dimensions of what is presumably the screen or window. Then the code checks if the doublebuffer (e.g. the backbuffer) exists, or if the backbuffer's width and height (dimensions) differ from the screen buffer (for example if user has resize its window or changed screen resolution). If the doublebuffer (backbuffer) does not exist or the dimensions are different, a new buffer is created with the same dimensions as the screen.
The code checks if the creation was successful (or that the doublebuffer exists), gets the graphics context of the buffer, calls paint() (where presumably all rendering is done) with the backbuffers graphics context, and then copy the backbuffer to the screen.
If, for some reason, the code was unable to create the buffer, the paint() method is called with the graphics context to the screen (I assume the variable g contains the graphics context to the screen), and rendering is done directly to this graphics context (e.g. to the screen).
I hope this helps.
Good luck with your game.
I have been developing on my Mac a JAVA Applications . The logic is as follows:
A Server sends to a client application some orders to Draw basic shapes
The client applications draws the the basic shapes into a Jpanel
Every Time a Shape arrives the program calls repaint()
public void paintShape(Shape p)
{
//this.paintComponent(this.getGraphics());
arrayofShapes.add(p);
this.repaint();
//this.updateUI();
//this.update(this.getGraphics());
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g2d = (Graphics2D) g.create();
g2d.setStroke(new BasicStroke(2));
g2d.setColor(pickedColor);
for(final Shape p : arrayofShapes)
{
g2d.draw(p);
}
//g2d.dispose();
}
Everything works smoothly(on real time) , so I decided to test the same application on a Windows computer. The result is a laggy application. These are the conclusion that I have reached.
RepaintManager is accumulating repaint() calls. I see how the shapes arrive at destination but in some cases more than 5 repaint calls are accumulated into one, which make the application very lagged/not real Time.
I have tried instead of calling repaint every time a shape arrives to do it with a Timer every few milliseconds, the result is the same. Code :
ActionListener listener = new ActionListener(){
public void actionPerformed(ActionEvent event){
repaint();
}
};
Timer displayTimer = new Timer(5, listener);
displayTimer.start();
In addition i have tested some random code that allows you to paint with the mouse, same logic as mine with paintComponent. In this case it work smoothly without sense of lag.... Example: http://javagraphics.blogspot.com.es/2010/06/shapes-implementing-freehand-pencil.html
I do not understand why paintComponent is so slow on my Windows Computer(same Jar). What could be affecting the performance of my program?
I have read all the answers regarding paint Components but any of them has solved this issue.
Any advice on how could I solve the problem and actually archive Real-Time?
Thank you in advance
Update Videos:
Mac Video:https://youtu.be/OhNXdGXoQpk real Time no problem handling heavy load
Windows Video https://youtu.be/yol2miHudZc clearly laggy
I apologize for the low quality
Update BufferedImage:
After introducing the BufferedImage the result is still a slow painting Application. It creates another problem, since one of the orders is to delete all shapes, it adds some complexity since I have to do a :
g2d.clearRect(0, 0, screenSize.width, screenSize.height);
HW/OS/JavaVersion
Windows
Processor i5-4300u 2.5ghz
Ram 12gb
Java version 1.7.0_71
MAC
Processor i7 2.9ghz
Ram 8gb
Java version 1.7.0_67
Java VisualVM
Video of live VisualVM:https://youtu.be/cRNX4b3rlZk
I do not see anything strange that could explain why the lag occurs but I'm far from being an expert(Again sorry for low quality)
Thank you for all your responses
There's no need to create() a new graphics context each time; just cast g to Graphics2D. This is safe on all concrete implementations. This also obviates the need to dispose() of the created context. As noted here, preserve any context variables that may be critical for later painting.
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
Stroke oldStroke = g2d.getStroke();
g2d.setStroke(new BasicStroke(2));
g2d.setColor(pickedColor);
for(final Shape p : arrayofShapes) {
g2d.draw(p);
}
g2d.setStroke(oldStroke);
}
Also, compare the profiles on both platforms to look for disparities. For reference, the example cited here comfortably handles selections containing hundreds of shapes on either platform.
I would recommend that you do static drawing to a BufferedImage, and then draw the BufferedImage in your paintComponent method.
e.g.,
private BufferedImage bufferedImage = new BufferedImage(IMG_WIDTH, IMG_HEIGHT, BufferedImage.TYPE_INT_ARGB);
public void paintShape(Shape p) {
Graphics2D g2 = bufferedImage.createGraphics();
g2d.setStroke(MY_STROKE); // make this a constant
g2d.setColor(pickedColor);
g2d.draw(p);
g2d.dispose();
this.repaint();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (bufferedImage != null) {
g2.drawImage(bufferedImage, 0, 0, null);
}
// do dynamic drawing such as drawing of moving sprites here
}
After more than two days of debugging I have found out that the problem has nothing to do with paintComponent()
With the same server generating random shapes
At the Windows app, some shapes are received at the same time which is impossible since I am sending shapes every 15 ms. That's why it accumulates shapes(result =lag).
On the other Hand at the Mac app, every shape has different time reception (result = real Time)
Thank you for the kind responses, and sorry for inconvenience I may have cause
I am following the intermediate java tutorial found at
https://www.youtube.com/watch?v=M2GoiOas2u8&list=PL54DB126285ED0420 .
But I am having issues with the double buffering flickering.
my relevant code in two of my classes is as follows
GamePanel.Java
private void gameRender()
{
if (dbImage == null) // create the buffer
{
dbImage = createImage(GWIDTH, GHEIGHT);
}
if (dbImage == null)
{
System.err.println("dbImage is still null!");
return;
}
else
{
dbg = dbImage.getGraphics();
}
// Clear the screen
dbg.setColor(Color.WHITE);
dbg.fillRect(0, 0, GWIDTH, GHEIGHT);
// Draw Game elements
draw(dbg);
}
// Draw all game content in this method
public void draw(Graphics g)
{
world.draw(g);
}
private void paintScreen()
{
Graphics g;
try
{
g = this.getGraphics();
if (dbImage != null && g != null)
{
g.drawImage(dbImage, 0, 0, null);
}
Toolkit.getDefaultToolkit().sync(); //For some OS.
g.dispose();
}
catch(Exception e)
{
System.err.println(e);
}
}
World.Java
public void draw(Graphics g)
{
for (int i = 0; i < arrayNumber; i++)
{
g.drawImage(blockImg[i], blocks[i].x, blocks[i].y, null);
}
}
Where arrayNumber = 1200.
This program only has flickering issues when the map is moved to the right or down, (meaning that blocks.x and blocks.y increase for each value of i), but not the other way around. I tried drawing from the bottom right up by replacing each [i] with [arrayNumber - i] and modifying the parameters of the for loop. This made the flickering only occur when moving the map up and to the left.
So I know that I should somehow modify this run method in the world class to somehow use double buffering, but was wondering how I would go about doing so since world doesn't extend JPanel?
Don't use this.getGraphics();, this is not how painting in Swing is done.
Swing uses a passive painting algorithm, which means you do not control the painting process. Swing will make decisions about what and when something should be painted.
Swing components are already double buffered by default.
Instead of fighting this, you should understand and embrace it.
Start by overriding paintComponent of you GamePanel and render your game state here (don't forget to call super.paintComponent)
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details about how painting is achieved in Swing
I have a program selection tool that i made. it opens a JFrame with 17 buttons, 15 of which are customizable, and they get their text from a .txt document located in the C: drive. when i click the assign button, it opens a JFileChooser to select a file to open when the button is clicked. You then select a button to change, and then type the text you want displayed by the button. After that the program rewrites the .txt file and updates the buttons. here is the code for updating:
public static void restart() {
start.assignButtonActions();
start.assignButtonText();
start.paint(graphics);
}
public void assignButtonActions() {
/**
* assign button actions
*/
for (int i = 0; i < buttonAction.length; i++) {
buttonAction[i] = io.readSpecificFromHD("C:\\ButtonActions.txt", i
+ 1 + actionButton.length);
}
}
public void assignButtonText() {
for (int i = 0; i < actionButton.length; i++) {
/**
* set button text
*/
actionButton[i].setText(io.readSpecificFromHD(
"C:\\ButtonActions.txt", i + 1));
}
}
public void paint(Graphics g) {
g.drawImage(getImage("files/background.png"), 0, 0, FRAMEWIDTH,
FRAMEHEIGHT, null);
refresh();
}
public void refresh() {
graphics.drawImage(getImage("files/background.png"), 0, 0, FRAMEWIDTH,
FRAMEHEIGHT, null);
for (int i = 0; i < actionButton.length; i++) {
actionButton[i].repaint();
}
assignButton.repaint();
helpButton.repaint();
}
Thats all the code that is required for this question i believe. The problem is, after the method restart() is called, the background is there, with a white square around the buttons, with it being white inside the square. not really a major problem, but really incredibly annoying and pretty unprofessional. At first i thought it was that the buttons were resizing after the background is painted, so i made it so that the refresh runs twice each time its called. didnt help one bit.
EDIT:
I fixed the problem. I took hovercraft's answer and modified what i learned a little bit. all i had to do was modify the restart() method to:
public static void restart() {
start.assignButtonActions();
start.assignButtonText();
start.repaint();
}
because the repaint(); repaint the whole component which was what hovercraft said. Thank you a ton everyone! hope this helps future questions.
You appear to be handling your Swing graphics incorrectly by calling paint(...) directly and trying to use a Graphics object outside of a JComponent's paintComponent(...) method. Don't do this, as all the Swing graphics tutorials will tell you (if you've not gone through some of them yet, you will want to do this soon). Instead do all graphics within a JComponent's (such as a JPanel's) paintComponent(...), call the super's method first, and use the Graphics object provided by the JVM in the paintComponent's method parameter.
Edit
Tutorial links:
The introductory tutorial is here: Lesson: Performing Custom Painting.
The advanced tutorial is here: Painting in AWT and Swing.
I'm thinking that you'll have to re-write most of your graphics code. Changes you should make:
Draw only in a JPanel or other JComponent-derived class, not in a JFrame or other top-level window.
Draw in your class's paintComponent(...) method.
Place an #Override annotation just above your paintComponent(...) method to be sure that you are in fact overriding the super method.
Call the super's paintComponent(...) as the first line (usually) of your paintComponent(...) override method.
Use the Graphics object passed into this method by the JVM.
Do not use a Graphics object obtained by calling getGraphics() on a component (with rare exceptions).
Do not give your class a Graphics field and try to store the Graphics object in it. The Graphics objects given by the JVM do not persist and will quickly become null or non-usable.
Do not call paint(...) or paintComponent(...) directly yourself (with rare exceptions -- and your current code does not qualify as one of the exceptions, trust me).
You will likely not need to call repaint() on your JButtons
I have made a custom component (derived from JComponent) which represents
a draggable Bezier-curve.
(looks like a hanging cable, someone might know it
from Bender or Cubase)
My problem is: The curve may become really long,
let's say from top left to bottom right corners of the desktop.
This makes Swing's repaint functionality inefficient:
The area of the curve is perhaps few hundred pixels, but the area of
the component (being mostly 'transparent') is millions of pixels big.
My subjection impression is:
The longer the curve, the more flicker I get when dragging it.
I hope I made myself clear about the problem.
Perhaps it would help when I somehow could choose by myself, which regions
of the component needs repainting at all.
EDIT:
Such a mess! I'm profiling the application using Netbeans, which helps to
find inefficient code normally, but this Swing framework is making hundreds
of nested calls! I just can't figure out, what is slow and why.
By the way, disabling super.paint(...) or super.paintComponent(...) doesn't help.
Check out Filthy Rich Clients by Chet Haase and Romain Guy. They address these very optimizations among others along the way to producing responsive and graphically impressive UI.
Doing all of your bezier mathematics on the paint thread everytime the component is refreshed is (as you've gathered) a bad idea. Does your curve change often? If not then why not paint it to a BufferedImage as and when it changes, and change your paint() code to simply draw the buffered image to the component instead.
class CurveComponent extends JComponent {
private BufferedImage image;
#Override
public void paintComponent( Graphics g ) {
if ( image == null ) {
return;
}
g.drawImage( image, 0, 0, this );
}
private void updateCurve() {
image = new BufferedImage( getWidth(), getHeight(), BufferedImage.ARGB );
Graphics g = image.getGraphics();
// draw the curve onto image using g.
g.dispose();
}
}
Only call updateCurve() when you need to and all that expensive mathematics won't be needlessly repeated. Painting should be pretty responsive, even for a fullscreen window. drawImage() will be doing a straightforward memory copy and should be lightning fast.
Try writing a tiny test app, which consists of nothing except what you need to reproduce this problem. This will make profiling easier. Then post that app here, so we can take a look at possible solutions.
I found your question interesting so I wrote a test app myself. This draws a Bezier curve which is continually resized as you drag. I created a gradient background to ensure this works well with a nasty background. I get good performance and low flicker, although I use top-notch machine.
It pays to read "Filthy Rich Clients" to learn all the tricks of writing custom Swing components that perform really well.
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Point2D;
public class CustomComponent extends JComponent {
private Point2D start = new Point2D.Double(0, 0);
private Point2D end = new Point2D.Double(300, 200);
private CustomComponent() {
this.setOpaque(true);
final MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
setEnd(e.getPoint());
}
};
this.addMouseListener(mouseAdapter);
this.addMouseMotionListener(mouseAdapter);
}
public void setStart(Point2D start) {
this.start = start;
repaint();
}
public void setEnd(Point2D end) {
this.end = end;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
final Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// draw gradient background
final int width = getWidth();
final int height = getHeight();
g2.setPaint(new GradientPaint(0, 0, Color.WHITE, width, height, Color.YELLOW));
g2.fillRect(0, 0, width, height);
// draw Bezier curve
final Shape shape = new CubicCurve2D.Double(start.getX(), start.getY(), start.getX(), end.getY(), end.getX(), start.getY(), end.getX(), end.getY());
g2.setColor(Color.BLACK);
g2.draw(shape);
g2.drawString("Click and drag to test for flickering", 100, 20);
}
public static void main(String[] args) {
final CustomComponent component = new CustomComponent();
final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
final Dimension size = new Dimension(screenSize.width - 20, screenSize.height - 100);
component.setPreferredSize(size);
final JFrame frame = new JFrame();
frame.add(component);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Some things to note:
only overwrite paintComponent(Graphics g), not the other paintXXX() methods
set custom component to opaque if possible
only use repaint() to request repainting. Never directly order a repaint directly in your code. This lets Swing handle it well.
There is no efficient way to create lots of small clip rectangles for a diagonal structure which leaves you with two strategies to avoid flickering:
Double buffering. This needs an enormous amount of memory but the memory copy is very fast (it usually happens in the time the "electron beam" goes back from lower right to upper left ... if there was still a beam in your LCD).
Don't call super.paint() (which draws or "erases" the background) and draw the curve a second time with the background color to erase it.
For more details, see this document.
[EDIT] If fillRect() wasn't abstract, you could set a break point :) Set a break point in paint(), check who calls it and whether the background got cleared at that time. It should be since rendering would be completely wrong. Then set break points further up in the call chain.
You can redraw a smaller portion of the screen using repaint(Rectangle r)
http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JComponent.html#repaint(java.awt.Rectangle)
Then you mention flicker. Since you are using swing, which uses double buffering your flickering must be coming from something else. Are you clearing the screen in paintComponent(...)? I.e. call to fillRect(...)? Don't do that, it's not needed (IIRC).
Which method do yo use to paint your curve? paint or paintComponent?
My solution was a partial re-design:
Now I don't represent each "cable"-element by a component.
Now, cables are just dummy objects (with no involved JComponent).
The repaint takes place "globally", on the content pane of the parent JFrame.
Now it's efficient, and flickers less.
just use getVisibleRect(); inside paintComponent(Graphics g) to get the area you actually need to redraw