I found this code of double buffering on internet but it has no explaination. I am a little confused in this code.
Why is the Image "i" used? What is its use if it is to be used once?
Why are we assigning changing color to Foreground color,when we already have set color?
What is g.drawImage() method doing?
Here is the code:
public void update(Graphics g)
{
if(i==null)
{
i=createImage(getWidth(), getHeight());
graph=i.getGraphics();
}
graph.setColor(getBackground());
graph.fillRect(0, 0, getWidth(),getHeight());
graph.setColor(getForeground());
paint(graph);
g.drawImage(i,0,0,this);
}
Regards
The basic idea of Double Buffering is to create the image off screen then display it all at once.
From the java tutorials found here
The code you have there first creates an image on first way through to be your "Back Buffer" with this bit, i is likely a field such as
private Image i;
private Graphics graph;
if(i==null)
{
i=createImage(getWidth(), getHeight());
graph=i.getGraphics();
}
Then Paints the background color onto the image with this
graph.setColor(getBackground());
graph.fillRect(0, 0, getWidth(),getHeight());
Then sets the front ready for drawing.
graph.setColor(getForeground());
paint(graph); /draws
Finally drawing the back Buffer over to the primary surface.
g.drawImage(i,0,0,this);
The graphics operations are all performed on a Graphics obtained from i, which is a bitmap in memory.
When they're finished, the bitmap is drawn onto the "real" (screen) Graphics object g. So the user will never see half-finished drawing, which eliminates flicker.
The field i is allocated the first time and then reused, so it is not only used once.
Related
I need to draw strings on a painted image. The project I am working on also requires the string to move around the screen at least 22 times in a second. Hence, its position can be anywhere on the image. So, redrawing the image with the string on it won't be possible as I feel there are better ways of doing this and that would unnecessarily consume resources redrawing the whole image. I have also tried using panel.getGraphics and then painting on the image but then all the drawn text is all over the screen(the code is below). I was wondering if someone could guide me in the right direction on how I can draw text over a paintedImage but it also needs to reset its position when required. The code I have tried that doesn't reset its previous position is below.
Original Panel with the Image:
public class PanelForImages extends JPanel{
private BufferedImage image;
public PanelForImages(File image) throws IOException{
//this.image = image;
//URL resource = getClass().getResource("so2.jpg");
this.image = ImageIO.read(image);
}
#Override
public void paintComponent(Graphics g){
//super.paint(g);
//super.paintComponents(g);
super.paintComponent(g);
//g.drawImage(image, 3, 4, this);
g.drawImage(image, 0, 0, getWidth(), getHeight(), this);
g.drawString("HELLOOOOOOOOOOOOOOOOOOOOOOOO", 400, 400);
//repaint();
}
}
Method with which I am trying to draw the string over the image.
public void drawFixationsOnFrame(GazeData gazeData){
Graphics g = this.panelForImages.getGraphics();
g.drawString("TEsting 123", (int)gazeData.smoothedCoordinates.x, (int)gazeData.smoothedCoordinates.y);
g.dispose();
jF.revalidate();
}
I have also tried making a new panel and then adding it to the current one but it doesn't seem to work. I am not sure how I can make it so it comes on top of the panelForImages without it hiding panelForImages.
The project I am working on also requires the string to move around the screen at least 22 times in a second. Hence, its position can be anywhere on the image. So, redrawing the image with the string on it won't be possible as I feel there are better ways of doing this and that would unnecessarily consume resources redrawing the whole image.
Then don't redraw the whole image. The JComponent repaint(...) method has an override, one that allows you to repaint only a select rectangle (check out the JComponent API for more on this). You will want to move the image in a Swing Timer, then repaint its old location (to get rid of the old String image) and repaint its new location. If you need the dimensions of the String, then use FontMetrics to help you get its bounding rectangle.
I note that your code has two other issues that are worrisome, some of it commented out:
Calling getGraphics() on a component to get its graphics component and draw with it -- you don't want to do this as this will result in a Graphics object that is short-lived risking broken images or a NPE
Calling repaint() from within the paintComponent. This is a bad and completely uncontrolled way of doing animation. Instead use a Swing Timer so you can have complete control of the animation and so you don't use paintComponent for something it was not intended to be used for.
I am playing around with graphics in Java using JFrames and am trying to completely understand when Graphics changes actually occur.
Let's say I have a timer running every second, and every second the graphics change. The tick method, for example, will run every second...
public void tick(Graphics g) {
g.fillRect(0,0,10,10);
//Do Some Computations
g.fillRect(0,0,someComputedWidth,someComputedHeight);
}
Are the graphics instantly updated on the computers display? When the first fillRect method is called, is that rectangle instantly created?
I want to be able to draw all my graphics, and then make them have their effect at the same exact time. I want the tick method to buffer all updates, and then execute them all at once. How do I accomplish this?
To buffer your updates you can use BufferedImage:
#Override
public void paintComponent(Graphics g){
BufferedImage image = new BufferedImage(this.size.width, this.size.height, BufferedImage.TYPE_INT_RGB);
Graphics bgr = image.getGraphics();
for(Tile t : this.tiles){
t.draw(bgr);
}
super(bgr);
g.drawImage(image, 0, 0, null);
bgr.dispose();
image = null;
}
I used this method in one of my school projects. It draws to an off-screen image and then draws the image to display.
With few drawable objects being drawn, and no, or little animation, updating directly to the display does not always have noticable effects, however with more complex tasks the image "flickers" and double buffering is a perfect cure for that.
I am displaying an image in Swing. I am drawing on top of the image (a bunch of drawRect() calls) and refreshing the screen. The image is constant, but the objects drawn on top are not. Is there any way to avoid redrawing the image any time? Since the graphics card likely does the image display, is it safe to assume that the drawRect() calls are the bottleneck? I draw as many as 20,000 calls a frame (but usually no more than 3000).
Edit: It is indeed the rect calls that are slowing it down and it can be made considerably faster by removing the transparency channel. That being said, it would still be nice to speed it up and include the transparency. The code can't really get simpler, so I am hoping by doing something different it will help.
public void paintComponent(Graphics g) {
super.paintComponent(g) ;
//grid or walkers
g.drawImage(image, 0, 0, null);
for(Walker w : walkArray){
g.setColor(new Color(255,255-w.data[3], 0, w.data[2]));
g.drawRect(w.data[0], w.data[1], 1, 1);
}
}
This is out of context, but can you lookup the colors in a precomputed palette instead of creating an instance of a Color in every cycle? Maybe that could improve performance a little.
Edit: For example, a List<Integer> is used as an RGB lookup table here, and a Queue<Color> is used here.
I found a rendering bug in some code and have found a workaround, but I would like to know why I am getting different behaviour. In the old code, the background would (sometimes) be rendered as white, despite while debugging getBackground() would return the correct colour.
Old code:
#Override
public void paint(Graphics g) {
// Stuff
g.setColor(getBackground());
g.clearRect(0, 0, width, height); // Obviously wrong.
// More stuff
}
New code:
#Override
public void paint(Graphics g) {
// Stuff
g.setColor(getBackground());
g.drawRect(0, 0, width, height); // Correct usage with 'setColor' call.
// More stuff
}
As I put in the code, it is obvious that setColor(getBackground()) has no effect on the clearRect(...) call. Yet I would assume that calling clearRect(...) and calling setColor(getBackground()) followed by drawRect(...) would be semantically the same.
I have also considered the opaqueness property, but the parent lightweight components and ancestor heavyweight component all use the same background colour, and it is quite obvious that this component is the one with the incorrect behaviour (it is one of 8 of the same type of component owned by its parent - yet only the ones that get to this section of code have a problem).
I am using JDK 1.6.0_07 (for business reasons of course) if that helps.
Here's the information from the JavaDocs -
Clears the specified rectangle by filling it with the background color of the current drawing surface. This operation does not use the current paint mode.
Beginning with Java 1.1, the background color of offscreen images may be system dependent. Applications should use setColor followed by fillRect to ensure that an offscreen image is cleared to a specific color.
As this implies, clearRect is system dependent and the value of getBackground() is not taken into account.
The difference is this :
if you use the graphics method fillRect() you can't erase the color by using drawRect() over the same recatngle specified in pixels.
but if you use the graphics method fillRect() then clear it with clearRect() and after that drawRect(); you reach a satisfaction and a conclusion.
How can I draw something in JPanel that will stay the same and not be repainted, I am doing a traffic simulation program and I want the road to be drawn once because It will not change.
Thanks
I'm not sure you actually want your road to never be repainted - repaint events fire (for example) when your window is resized, or when it becomes visible following another window obstructing it. If your panel never repaints then it'll look peculiar.
As far as I remember, Swing will only fire appropriate paint events for these circumstances, so you should be OK following the usual method of subclassing JPanel with a suitable override:
public class RoadPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// your drawing code here
}
}
If you cache your road into an image or another graphics format (to save calculating the display data multiple times) once drawn once, this might save you some time on subsequent paints.
To my knowledge, no, unless there is a trick with transparent overlays.
Most graphical applications I saw (and did) just re-draw the whole panel on each repaint. Now, you can do that once, in a graphic buffer, and then just paint the whole background at once, quickly, by copying the graphic buffer to the JPanel. It should be faster than calling all graphical primitives to draw the road.
Or, the way some 2D games do, perhaps paint it once and update the moving parts, like sprites: it needs to erase the old place used by the sprites (restore the background there) and re-draw the sprites at the new place. So you still have a copy of the road in a graphic buffer but instead of re-drawing it whole each time, you update only some small parts. Can be slightly faster.
The component will need to be repainted every time that the panel is obscured (ie frame minimized/another window put on top). Therefore drawing something only once will not work as you want it to. To make parts that do not change be drawn more efficiently you can draw them once to a 'buffer' image, and then just draw this buffer each time that the panel or component needs to be redrawn.
// Field that stores the image so it is always accessible
private Image roadImage = null;
// ...
// ...
// Override paintComponent Method
public void paintComponent(Graphics g){
if (roadImage == null) {
// Create the road image if it doesn't exist
roadImage = createImage(width, height);
// draw the roads to the image
Graphics roadG = roadImage.getGraphics();
// Use roadG like you would any other graphics
// object to draw the roads to an image
} else {
// If the buffer image exists, you just need to draw it.
// Draw the road buffer image
g.drawImage(roadImage, 0, 0, null);
}
// Draw everything else ...
// g.draw...
}
What I do is set a boolean value to whether or not a certain part needs to be redrawn. Then, in the paintComponent() method I can check the value and redraw the certain thing, or not.
protected void paintComponent(Graphics g){
super.paintComponent(g);
if (drawRoad) {
drawRoadMethod(g);
}
drawTheRest(g);
}
Kinda like that.