How to wait until JComponent is fully painted? - java

I need a way to wait until a (Swing) JComponent is fully painted. This actual problem arises from an openmap application: The task is to draw a map (mapBean) with a couple of layers and create an image from that map.
Unfortunatly, and it's clearly documented, the image formatter takes the current state from the map to create the picture, and there's a chance, especially when maps become complex, that the formatter ist called before the mapBean, a JComponent, is painted.
Although explained with this openmap application, the problem is quite general and supposedly Swing related. Right now, I just wait a fixed time (one second) but that does not eliminate the risk of creating incomplete maps...
Edit
Some more details - I have to start with constructing a (OpenMap) MapPanel, which internallz creates a MapBean (JComponent subclass) and a MapHandler. Then I feed the MapHandler with geographical Layers and the Framework starts painting the geographical data 'on' the JComponent type MapBean.
After adding all layers to the Map, I use another framework class to create a JPG image (or: the byte[] that holds the image data). And this can cause problem, if I don't wait: this 'image creator' creates the image from the current state of the map bean, and if I call this 'image creator' to early, some map layers are not painted and missing. Pretty annoying...

java.awt.EventQueue.invokeLater will allow you to run a task after the paint operation has finished. If it is doing some kind of asynchronous load, then it will be API specific (as MediaTracker does for Image).

You might also try using an off screen buffer to render your image in:
MapBean map = new MapBean();
map.setData(...);
BufferedImage bi = new BufferedImage(...);
map.paintComponent(bi.getGraphics());
writeToFile(bi);

It sounds like you need to synchronize updating your JComponent's underlying data with Swing's paint cycle. You can subclass your JComponent and decorate the paintComponent() method, you might also look at ImageObserver.imageUpdate(), though I'm not sure if that is going to tell you what you want.
public class DrawingCycleAwareComponent extends MyOtherJComponent {
private final Object m_synchLock = new Object();
protected void paintComponent(Graphics g) {
synchronized (m_synchLock) {
super.paintComponent(g);
}
}
protected void updateData(Object data) {
if (SwingUtilities.isEventDispatchThread()) {
reallySetMyData(data); // don't deadlock yourself if running in swing
}
else {
synchronized (m_synchLock) {
reallySetMyData(data);
}
}
}
}

Related

Java Swing brief paintComponent graphics

I am creating a java 2D game and have come to a point where I am thinking of creating "pop-up graphics" i.e graphics that will populate the screen when a certain event occurs. Say, you pick up a certain item, I want this item to be displayed in the middle of the screen in a box containing information about said item.
Currently I have one big paintComponent that paints all of the graphics for the game (tiles, entities, players etc (which has been done efficiently I might add)). I know that I can probably have a boolean value which checks if an item has been picked up in that method, but it feels wrong.
What I am wondering is, is there a way for items to have their own paintComponent so that when it is called, it will show say a bo for a brief period WITHOUT having a boolean value in the big paintComponent method that I currently use for everything?
Small code example (won't execute)
public class popUpGraphics extends (JComponent or JPanel or whatever works best for this scenario)
{
public popUpGraphics(){
}
#Override
protected void paintComponent(Graphics g){
//g.Draw(stuff);
}
}
and then somewhere in an event I instantiate this or somesuch.
I do not want this to override the other paintcomponent, I just want to add to it
(as if it was another layer) to the paintComponent
In short I want to know:
1. Is is possible to have brief graphics shown without including in the huge paintComponent method
2. What Swing library should be extended (JComponent, JPanel etc)
Thanks in advance!

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").

How do I let multiple Threads paint onto an AWT component?

EDIT: solved, look below for my solution.
first of all, this is my very first question here, so if I make any mistakes, please tell me.
I am trying to write a Mandelbrot Fractal Program in Java, for training purposes. The ideal for all the functionality I want to have is the Fractalizer (http://www.fractalizer.de/en/), but for now, I will be happy with a program that draws the Mandelbrot Set on the screen (instead of, for example, writing it to an image file). Of course, I want the program to be fast, so I thought that I could split the calculation into multiple threads to utilize my multi-core processor; for example, on a quad-core system, the image would be divided into 2x2=4 images, each to be calculated by a separate thread. All these threads get a Graphics object passed on which they draw the pixels as they are calculated.
My first attempt to get this working was to have the threads draw on a BufferedImage.getGraphics() and to have the paint() method constantly call repaint() as long as the image isn't finished:
g.drawImage(tempImg, 0, 0, null);
if (waiterThread.isAlive())
{
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// do nothing
}
repaint(10);
}
(waiterThread joins all calculating threads one after another, so as long as the waiterThread is alive, at least one calculating thread is not yet finished.)
This works, but causes ugly flickering on the canvas because of the frequent repainting.
Then, by means of a small test program, I found out that Graphics.draw*anything* draws on the screen instantly, before the paint method returns, so my current approach is the following:
One Panel with a GridLayout that contains 2x2 (on a <4-core system, 1x1) MandelbrotCanvas objects
Each MandelbrotCanvas object will, on the first paint() call, initialize a calculating Thread, pass its own Graphics object to it (actually, I'm using a custom GroupGraphics class that passes one Graphics call to several graphics, to "backup" the image into a BufferedImage.getGraphics(), but that's not important), and start the calculating thread.
The panel will in its paint() method fetch the calculating threads from each of the MandelbrotCanvases and join() them.
Unfortunately, this creates only a black screen. Only when calculation is finished, the image is displayed.
What is the right way to have several threads paint onto one component?
EDIT:
What I didn't know: Only the Event Dispatch Thread is allowed to paint on AWT components (roughly spoken), which means that the last approach above can't possibly work - apparently, it's supposed to throw an exception, but I didn't get one. My solution is to use the first approach - draw the image onto a BufferedImage and draw that onto the Canvas - with the only modification that I overload the update() method to call the paint() method without clearing the painting area:
public void update(Graphics g)
{
paint(g);
}
So I guess my answer to the general question ("How do I let multiple Threads paint onto an AWT component?") would be: You can't, it's not allowed. Let the Threads draw onto a BufferedImage.getGraphics(), and draw that image repeatedly. Overload the update() method like above to avoid flickering. (It looks really great now.) Another tip that I can't use in my case, but is still good, is that there is a variant of repaint() that takes rectangle arguments to specify the area that has to be redrawn, and a variant that takes a time argument (in milliseconds), so the repaint doesn't have to happen immediately.
EDIT2: This link provides very helpful information: http://java.sun.com/products/jfc/tsc/articles/painting/index.html
Only the GUI thread can paint directly on your component.
So you must call the repaint method.
When you have background computation, to force a fast drawing, you should use the version taking a time as parameter.
Some details from here :
NOTE: If multiple calls to repaint() occur on a component before the
initial repaint request is processed, the multiple requests may be
collapsed into a single call to update(). The algorithm for
determining when multiple requests should be collapsed is
implementation-dependent. If multiple requests are collapsed, the
resulting update rectangle will be equal to the union of the
rectangles contained in the collapsed requests.
You have to send requests to the EDT.
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Rectangle r = myCurrentWorkingThread.getFinishedRectangle();
myPainter.repaint(r);
}
});
The idea is that you won't repaint pixel by pixel, rather giving larger chunks to the worker-threads. As soon as they're finished with a unit of work, they notify the main object (myPainter) that would do the actual work. This construct (EventQueue.invokeLater) will guarantee that it will be on the Event Dispatcher Thread.

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.

Painting javax.microedition.lcdui.Graphics on LWUIT Component

What would be the best method for getting a custom element (that is using J2ME native Graphics) painted on LWUIT elements?
The custom element is an implementation from mapping library, that paints it's content (for example Google map) to Graphics object. How would it be possible to paint the result directly on LWUIT elements (at the moment I am trying to paint it on a Component).
Is the only way to write a wrapper in LWUIT package, that would expose the internal implementation of it?
Edit:
John: your solution looks like a lot of engineering :P What I ended up using is following wrapper:
package com.sun.lwuit;
public class ImageWrapper {
private final Image image;
public ImageWrapper(final Image lwuitBuffer) {
this.image = lwuitBuffer;
}
public javax.microedition.lcdui.Graphics getGraphics() {
return image.getGraphics().getGraphics();
}
}
Now I can get the 'native' Graphics element from LWUIT. Paint on it - effectively painting on LWUIT image. And I can use the image to paint on a component.
And it still looks like a hack :)
But the real problem is 50kB of code overhead, even after obfuscation. But this is a issue for another post :)
/JaanusSiim
I do not think any hacking is necessary. You can subclass the LWTUI Component class and then you can pain whatever you want on to the graphic context of the component. You do not get the native lcdui.Graphics object but an object with a same interface that is easy to use.
If you really need to pass a lcdui.Graphics to some underlying library to display its output then I would suggest this:
Somewhere in your component code (do only when the component contents really need to be changed):
private Image buffer = null; // keep this
int[] bufferArray = new int[desiredWidth * desiredHeight];
javax.microedition.lcdui.Image bufferImage =
Image.createEmptyImage(desiredWidth, desiredHeight);
thirPartyComponent.paint(bufferImage.getGraphics());
bufferImage.getRGB(bufferArray,0,1,0,0,desiredWidth, desiredHeight);
bufferImage = null; //no longer needed
buffer = Image.createImage(bufferArray, desiredWidth, desiredHeight);
In the component paint(g) method:
g.drawImage(0,0, buffer);
By doing the hack you did you are losing portablity and also sice you are exposing implementation private object you might also break other things.
Hope this helps.
Based on the javadoc for LWUIT and J2ME and guessing that the custom J2ME class is a Canvas it looks like you would have to:
Subclass LWUIT's Component class wrapping the custom J2ME component
Override the paint() method of the LWUIT Component
Subclass the J2ME Graphics class wrapping the LWUIT Graphics class and pass all the method calls through
Pass in the wrapped J2ME Graphics implementation to the custom J2ME component's paint method
That third step is an ugly one. Check on the LWUIT mailing list to see if anyone has dome this before. From the published APIs I don't see another way to do it.
Edit: The hack added in the question looks better than my hack for an Image. What I have may be better for a general case, but I don't know either LWUIT or J2ME well enough to really say that.

Categories

Resources