Swing graphics in multithreaded program - java

I'm coding a game; something like this: http://i.stack.imgur.com/HBrEE.png
I have a class named Canvas which extends JLabel and in overriden paintComponent(), put an image of gridlines as background, then paint the player and obstacles (walls).
By using createGraphics() , I get a Graphics2D object. I need this to paint weapons and bullets from their classes, too. (As you see, there are several weapons that shoot bullets in specific time intervals.)
For implementing all these, using SwingUtilities.invokeLater() I paint the gridlines, player and walls in Canvas class. I thought of creating a thread per weapon and one per bullet; I know, it's an overkill but since I'm new to multithreading I have no other ideas!
And another problem is with Graphics2D object: I need to share this object between threads so I thought of using final keyword; but it's not possible because the value of Graphics2D object will be determined in paintComponent().
Thanks in advance for any help you are able to provide.

You shouldn't use multi-threaded rendering threads ever (well, in 99% of the cases at least). Most graphics programs keep the full scene state in some object tree that only one thread renders every so often (1/60th of a second). The rendering thread goes through each object in the render tree and invoke .paint or whatever else will get the object to be drawn. Rinse and repeat and you're done.

Related

Is Graphics2D thread safe?

I'd like to improve the drawing performance of my code onto a java.awt.Graphics2D instance that I've aquired from a BufferedImage. (Swing and UI are not in the picture at all). I am struggling to find a clear answer if an instance of Graphics2D is thread safe or not, ie. can I set out N different threads drawing on the same instance or not?
Well, Graphics2D and Graphics are abstract classes without any state so in theory they might be threadsafe but that would depend on the actual implementation/subclass you get.
However, if your drawing code might result in overlaps handling multiple threads might get quite complicated (if you can order the shapes and there's no transparency involved you might be able to use some z-buffering). Thus I'd not bother with multithreaded rendering onto the same canvas (graphics object) which would render the question moot.
Instead I'd suggest splitting rendering into muliple discrete tasks and combining the results in the end.
Depending on what you're actually rendering I could imagine 2 basic approaches:
split rendering into multiple tiles and combine them at the end
if you're rendering complex shapes which are costly to generate then render them into intermediate images and then have one thread combine them by just rendering those intermediate images onto the final canvas (this might require ordering the intermediate steps)

How do I create a new thread(s) that draws to the screen in Java?

Right now I have a main game loop that constantly redraws the screen. Since I need to slow this thread down but continue to draw other items at a faster rate I need to make a new thread. The problem is I am not sure how to go about making a new thread that also draws to the screen I know how to make a new thread, I am just stuck on how to implement the Graphics2D drawing in the new thread. For example I have the code below which is the typical starting point and then there is the draw method defined in the other class that directs what and when to draw. If I wanted to branch off and have another thread drawing and doing its own thing how do I do that?
Do I have to make a new class that creates an entirely new PaintComponent()? Or would I simply create a new Graphics2D object so I can use different font colors and such? I guess what confuses me most is that I can't just call a different draw method because I still need to pass g2d as the argument. So it appears to me that I need to make the thread from within another method that already has the g2d object.
If this is confusing I do apologize as I am still a beginner to JAVA. If you need more information just let me know. Thank you in advance.
public abstract void draw(Graphics2D g2d);
#Override
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
super.paintComponent(g2d);
draw(g2d);
}
In the first place, Swing is inherently single-threaded. This was once summarized in the "single thread rule"
Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
(Unfortunately, the respective site did not survive the migration of Java from Sun to Oracle, but some information can be found here http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html or when doing a websearch for "swing single thread rule")
In general, this applies also to painting : The paintComponent may only safely be called by the Event Dispatch Thread (EDT). And it will be called "automatically". That is why this technique is called "passive rendering": You overwrite the paintComponent method, expecting that it will be called by the EDT.
However, particularly for game development, you can use a technique called "active rendering". In this case, painting is slightly more complicated and involves setting up an own BufferStrategy. The potential advantage is that any thread may perform rendering operations in this case, because you can obtain a Graphics object by calling BufferStrategy#getDrawGraphics.
Information can be found at http://docs.oracle.com/javase/tutorial/extra/fullscreen/rendering.html (while this refers to fullscreen rendering, similar concepts can be applied to active rendering in a window, but I'd recommend to consult further tutorials/resources that can be found with keywords like "swing active rendering").

Combine two graphics objects in Java

I got two Graphics objects, is it possible to combine them into one?
The answer of Hovercraft Full of Eels is useful, but not what I meant. Say I have two Graphics objects, private Graphics gr1 and private Graphics gr2. Now, how should I merge them in the paintComponent(Graphics) method to draw them to my (for example JPanel)?
The question is not at all about images, but about mere Graphics objects. Sorry, the first paragraph was misleading.
Edit:
In response to Hovercraft Full of Eels:
If you draw in the paintComponent() method, I find it annoying everything is gone if you redraw your screen, and if you want to move something you have to save the coordinates and dimensions of everything, adjust those and get them someway in your Graphics object.
The question I asked myself was accely: What object is best suited or saving a Graphics object. A Graphics object, I thought. but the problem is that if you have (for example) two rectangles, and one moves to the left and one moves to the right you can't if you have 1 Graphics object.
My solution was multiple graphics objects, which can be transformed to simulate movement and then drawn to the screen.
#Hovercraft Full of Eels I think you (and most SO'ers) think this is not a good solution, looking at your answer.
But, an answer saying You're doing it all wrong, you'd better stop programming doesn't help me at all, so please give me an alternative.
You can draw any image into a BufferedImage by getting its Graphics context, drawing in it, and then disposing of the Graphics object. For example
// get a Graphics object from BufferedImage, img1
Graphics g = img1.getGraphics();
// use that g to draw another BufferedImage starting at x, y of 0, 0
g.drawImage(img2, 0, 0, null);
// clean up resources so as not to run out.
g.dispose();
Now img1 shows old img1 with img2 overlying it. If you want the images to be transparent, you'll need to read up on alpha composite and how to use it
Edit
Regarding your edited question:
I got two Graphics objects, is it possible to combine them into one?
The answer of Hovercraft Full of Eels is useful, but not what I meant. Say I have two Graphics objects, private Graphics gr1 and private Graphics gr2. Now, how should I merge them in the paintComponent(Graphics) method to draw it to my (for example JPanel)?
The question is not at all about images, but about mere Graphics objects. Sorry, the first paragraph was misleading.
This request is somewhat confusing to me since I think of Graphics objects as pens or paintbrushes that are used to draw something to an image or the screen. So I don't think that you draw a Graphics object to anything, but rather that you use a Graphics object as a tool to draw to something else that can display or store the graphics. You are going to have to put a lot more effort into asking your question, supplying us with enough details so that we don't have to keep guessing what it is you are trying to do. Please tell us the whole story.
Edit 2
In response to your latest edit to your question:
If you draw in the paintComponent() method, I find it annoying everything is gone if you redraw your screen, and if you want to move something you have to save the coordinates and dimensions of everything, adjust those and get them someway in your Graphics object.
The question I asked myself was accely: What object is best suited or saving a Graphics object. A Graphics object, I thought. but the problem is that if you have (for example) two rectangles, and one moves to the left and one moves to the right you can't if you have 1 Graphics object.
No, a better solution I think is to use a BufferedImage to save that which should persist. When ever you wanted to add an image to the background BufferedImage, you'd obtain the BufferedImage's Graphics object by calling getGraphics() on it, then you'd draw your image to it by using the Graphics object's drawImage(...) method, then you'd call dispose() on the BufferedImage's Graphics object so as not to lose resources.
To draw the background BufferedImage in the background, you'd call it at the top of the paintComponent(...) method, usually just after calling the super.paintComponent(...) method.
My solution was multiple graphics objects, which can be transformed to simulate movement and then drawn to the screen.
#Hovercraft Full of Eels I think you (and most SO'ers) think this is not a good solution, looking at your answer.
Yep, you guess correctly -- I believe that there are better solutions than the one you suggest.
An example of just what I'm getting at can be found in my code post in my answer to this question: changing-jpanel-graphics-g-color-drawing-line.
If you run my code, you'd see this:
To impose images, use an appropriate layout, as shown here.
To compose images, use an appropriate composite rule, as shown here.
Addendum: #HFOE is correct. An instance of Graphics or Graphics2D, sometimes called a graphics context, is a transitory means to apply rendering methods; the target device or off-screen image must exist independently. Classes implementing the Shape interface are good candidates for encapsulating information about what to render in a given context. AffineTest and GraphPanel are examples.

java 2D and swing

I have trouble understanding a fundamental concept in Java 2D.
To give a specific example:
One can customize a swing component via implementing it's own version of the method paintComponent(Graphics g)
Graphics is available to the body of the method.
Question:
What is exactly this Graphics object, I mean how it is related to the object that has the method paintComponent? Ok, I understand that you can do something like:
g.setColor(Color.GRAY);
g.fillOval(0, 0, getWidth(), getHeight());
To get a gray oval painted. What I can not understand is how is the Graphics object related to the component and the canvas. How is this drawing actually done?
Another example:
public class MyComponent extends JComponent {
protected void paintComponent(Graphics g) {
System.out.println("Width:"+getWidth()+", Height:"+getHeight());
}
public static void main(String args[]) {
JFrame f = new JFrame("Some frame");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(200, 90);
MyComponent component = new MyComponent ();
f.add(component);
f.setVisible(true);
}
}
This prints
Width:184, Height:52
What does this size mean? I have not added anything to the frame of size(200,90).
UPDATE:
I understand that I must override paint to give in the Graphics g object the hints required to do the repaint and that I do not have to create a Graphics object as one will be given by platform.
What happens after that is what I can not understand.
E.g. does Graphics represent the screen and the object is painted accordingly on screen as soon as I start calling the various g.setXXX methods?
Does it get stored in a queue and there is a 1-1 association among g and each component? So the framework uses each g of each component to paint it one at a time?
How does this work?
Any help on this is highly welcome
Thanks
I understand your problem as I struggled with it for some time when I was learning Java graphics. It's not just Java 2D graphics - it is part of AWT.
When you create a JFrame or some other top-level object, it does a lot of work "behind the scenes" - part of which is creating a Graphics object. (There is not explicit notification of this, though if you stepped through the code with a debugger you may see classes which create the Graphics).
You then create components which you add, or register with, the top-level object. These all have to implement a call-back method, including
paint(Graphics g);
You will then #Override these methods so that when the component is rendered it uses YOUR paint method.
Do not try to save the Graphic or create a new one. Think of it as the framework taking the responsibility off you.
The size of components is often taken out of your hands. If you use a layout manager then it may decide to resize your component.
If you are coming from a procedural imperative background you may well have problems (I came from FORTRAN). My advice would be to try a number of tutorials and - at some stage - enlightenment will start to come.
The general documentation for Java graphics is poor. There are many concepts which are opaque (see How does Java Graphics.drawImage() work and what is the role of ImageObserver ). The early implementation was rushed through and had many bugs. Even now it is often unclear whether and in what order you should call methods such as setPack() and setVisible().
This doesn't mean you shouldn't use it! Just that the learning curve is a bit longer than IMO it should be.
MORE:
Also YOU don't decide when something is painted, the framework does. paint(g) really means "The framweork is repainting its components. What to you want this component to provide at this stage".
Maybe providePaintingInstructionsWhenRequiredForComponentGraphics(Graphics g) would be a useful name.
Similarly repaint() does not repaint at your orders, but when the system thinks it should. I haven't found it useful.
If you (say) resize a component interactively every slight change will normally trigger a paint(g). try putting a LOG.debug() in the paint code and seeing when it gets called.
What does this size mean? I have not added anything to the frame of size(200,90).
You added your component to the frame and set the size of the frame to be (200, 90). The default layout manager for the content pane of the frame is the BorderLayout, which means the component you added gets all the available space. The frame needs some space for the title bar and borders, so your component gets the remaining space.
The component does not create a static Graphics object association.
The graphics object is the wrapper for a platform handle giving access to a physical device, like the screen. It's valid for the time when "paint" is executed only, you can't store it and reuse it later. It is a resource managed by the "toolkit".
The component itself is an abstraction on top of the windowing system, that gets asociated shortly with this device for getting rendered.
EDIT
You can force such an association calling "getGraphics" if you feel the need to paint out of the "paint" callback. This should be a very rare case and you ALWAYS should dispose the Graphics afterwards.
Think of a Graphics like a piece of paper which you draw on to show how the Component looks like at that moment. After you've drawn it, the framework toolkit will trim off the edges and show what you've drawn to display the component. Moreover, the next time you draw the component, you'll be drawing on a different piece of paper, so don't keep the old Graphics around.

Is this bad practice? Multiple Graphics2D Objects

I've created a JPanel canvas that holds all graphics; namely JLabel. To get animated sprites to work you have to over ride the paintComponent of the extended JLabel class. I've successfully implemented animated sprites this way.
Is it bad practice to have a Graphics2D canvas and then have multiple 'images' in their own Graphics2D?
I don't think it will be too much heavyweight since the Graphics2D of your JPanel should be the same one that is passed to the JLabel but with different bounds and offsets.
What I mean is that Swing doesn't allocate a new graphics context on which you can display for every element inside a hierarchy of objects but it uses the same with different capabilities. This doesn't mean that panel.getGraphics() == label.getGraphics() but neither they are completely different obects.
In any case, if you need to do much animated work I would suggest you to have your own sprite class
class Sprite
{
Point2D position;
Rectangle2D size;
float rotation;
}
and handle everything at the same paintComponent level. Or at least I've always did that way since Java2D is not like CoreAnimation that is made to be used on a per-layer basis for moving/animated content.

Categories

Resources