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

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.

Related

Integrating logic into paintComponent

I am currently studying Swing and have hit a mental roadblock. I know when I want to paint to a JPanel I need to call the paintComponent method. I have read several places that logic should not be located within your paintComponent block. If I want a timer to determine when something is painted, would I not have to call paintComponent from another method of another class and create an instance of that class that also extends JPanel within the paintComponent?
I think what I am trying to avoid is the following...
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawImage(blah,blah,blah);
if(timesUP==true){
paintSomethingElse(g);
}
repaint();
}
Instead I get the impression I should be creating a completely different method for the logic of the if statement and somehow paint from a separate method.
What am I not understanding?
Has this already been specifically answered? I was unable to find an answer that helped me really understand how to separate the two efficiently allowing multiple graphics to be drawn on the same JPanel.
It depends. If the logic is related to the actual painting process, there's no reason why you couldn't put it into the paintComponent method.
What you want to avoid is putting logic in the paintComponent that changes the state of the paint process, as paintComponent could be called for any number of reasons, many of which you don't have control over.
The paintComponent method should paint the current state of the component, that's all, it should not be involved in changing or updating that state (this could actually lead to an infinite repaint loop which will consume your CPU cycles)
I have also had to make a Java Swing application which had a lot of painting to do - the JPanel had to paint multiple custom components which moved around on the screen as the user interacted with them.
The paintComponent method is there for drawing and that should be the end of it. Conditional painting is, from my side, doable in the paintComponent as long as you are not waiting for conditions, setting states of other objects or anything which might on the one hand slow you down a lot and on the other send you into waiting for ever. I call the logic here reaction logic.
On the other hand, don't forget that the repainting can be triggered externally, any time you might need it. This way you can separate the logic from painting to some extent. Use listeners, create your own events, but keep action logic out of the paintComponent. You can easily find a way to set states before entering the painting, so that when you decide to paint, you only react to the state.
Don't forget: if you are multi-threading, painting might get messy if you work on half-set states of objects. Thread carefully!
If the logic is very complex and may be too slow for calling from inside the paintComponent (all GUI of your app is stalled while this method is running), you should define a separate update method to prepare precomputed boolean flags, coordinates, images and other values that paintComponent could use to produce the final view quickly enough.
Such update method can run in a separate thread. You should call repaint() or (if layout may change) invalidate() at the end of this method. Use a separate object for all prepared values and make sure that painting thread and updating thread do not access the same instance a once.
If the logic is fast enough, I see no problem in defining it inside the paintComponent method. If the logic is complex but still fast enough, define it in some methods that are called from paintComponent directly.

use super.paintComponent(g) or getGraphics() in java

i' m little bit confused about few things:
Example code,that shows my problem,this isn't compilable
// image
private BufferedImage image;
private Graphics2D graphic;
private changeImage;
. . .
//thread loop
while (running) {
. . .
render();
Graphics showGraphic = getGraphics();
showGraphic.drawImage(image, 0, 0, null);
showGraphic.dispose();
}
public void render(){
if(changeImage == 1)
graphic.drawImage(ImageLoader.TREE, 0, 0, null);
else if(changeImage == 2){
graphic.drawImage(ImageLoader.HOUSE, 0, 0, null);
. . .
graphic.fillRect(50,60,30,40);
}
}
I create an global object Graphic2D and i draw things in render(), I do not call repaint() inside the game loop, is it good practice to do this?
Should I use repaint() inside my loop , and the paintComponent() function ?
one other thing, how graphic.dispose() works correctly? , because trying to remove this line of code, nothing happens.
I understand how works dispose() according to java docs, but I have not noticed any differences with dispose() or without.
my program runs very well, but i have this dubts.
is it good practice to do this
No, this is actually incredibly horrible and error prone. This assumes that this is the Component#getGraphics method. The problem is Swing uses a passive rendering algorithm, that is, Swing decides when and what should be repainted and does this for optimisation reasons.
This means updates are not regular, which really helps when doing animation, and can happen at any time for any number reasons, many of which you simply don't and can't control.
For these reasons, painting should be done within one of the paint methods, preferably, paintComponent of a JComponent based class. This way, when Swing decides to do a repaint, one, you know about it and can update the output accordingly, and two, it won't clear what you have previously painted to it using getGraphics which could cause flickering...
You are also running into potential threading issues, as you thread is trying to paint to the Graphics context, so might the Event Dispatching Thread and that isn't going to end pretty...All painting should be done from within the context of the EDT (for component based painting)
You could try using a BufferedStrategy, which would give you direct control over the painting process, but this limits you to the AWT library. Don't know if this is good or bad in your case.
one other thing, how graphic.dispose() works correctly? , because
trying to remove this line of code, nothing happens.
dispose basically releases any native resources that the Graphics context might be holding. General rule of thumb, if you didn't create, you don't dispose of it.
So if you use Graphics#create or BufferedImage#createGraphics to obtain a Graphics context, then you should call dispose when your done with it.
On some systems calling dispose on the Grpahics context used to perform painting of components (such as that passed to paintComponent or obtained from getGraphcis) can prevent further content from been painted.
Equally, not disposing of Graphics contexts that you have created can lead to memory leaks as they won't get garbage collected...
There are many questions about "good" or "correct" painting in Swing, so this question could be considered as a duplicate, and I won't try to repeat here what should be read in context, for example, in the Lesson about Performing Custom Painting.
However, to summarize the most important information short and concisely:
You should never call getGraphics on a Component. It may return null or a Graphics object that is in any other way "invalid". It will fail in the one form or the other, sooner or later
You should do all your painting operations in the paintComponent method (or in methods that are called from paintComponent), using the Graphics object that you receive there as an argument
Whether or not you call the Graphics#dispose method may not make a difference that is "immediately" visible. You should not call it on the one that you received in the paintComponent. You should only call it on a Graphics object that you either obtained from a BufferedImage, or one that you created by calling Graphics#create(). When you do not call it, this might cause a memory leak that will only be noticed after the application has been running for a while.

Does the repaint() get called automatically sometimes?

I'm making a game. Each object in the game is registered in a registry. I have an update and render method being called by a game loop. The render method calls repaint() on my JPanel and I have overridden paintComponent(Graphics g) for graphics. When it renders and updates, it goes through each object and updates it and renders it from the registry. Sometimes I get a concurrent modification error, and i believe it is because something is calling the paintComponent() other than from my game loop. If a JPanel or even JFrame calls repaint on its own, is there any way to disable it?
No, repaint() doesn't get called automatically, but paint(Graphics g) does at times, and you have no control over this. Also, your repaint() calls may be ignored if they get "stacked". For more on this, please check out this article: Painting in AWT and Swing.
Hopefully you have no program logic inside of your paintComponent method. If you do, get it out of there.
"paintComponent() other than from my game loop"
First, you should never be calling any paint method yourself (even printing should be done via the print methods).
Second, if you call repaint, a paint event will be scheduled by the repaint manager and (eventually) processed by the Event Dispatching Thread. When and how this is done comes down to how the repaint manager schedules these events. This means that all painting is done within the context of the EDT (unless you are doing something you shouldn't be). This means painting is never done from within the context of your game loop
This means, if your changing the state of the game that the paint routines rely on, you will have concurrent issues.
You could synchronize the shared data structures so that they can't be accessed while some other thread is accessing the data.
You could render the state of the to a backing buffer within the game thread, then assign it to a active buffer. Thus is kind of like a double buffering approach

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

I get weird black areas in a BufferedImage, how can I fix this?

I am currently writing a Fractal Explorer program, and I am encountering a really weird issue with it: I am drawing the fractal on a BufferedImage, and I get random black areas in that image. Screenshots: http://imgur.com/a/WalM7
The image is calculated multi-threaded: The big image is split into four (because I have a four-core processor) sub-images that are calculated individually. The black areas appear at the beginning of each of the sub-images. They are always rectangular, not necessarily following the order in which the pixels are calculated (left-to-right, but the area does not always stretch to the far side of the sub-image).
I have verified that immediately after the pixel is drawn (with Graphics.drawLine), BufferedImage.getRGB returns the right color for the pixel, but after the calculation is finished, it can return black instead, as the pixel is drawn on the screen.
The problem seems to vanish if I disable multi-threaded calculating (by assigning only one core to javaw.exe via the task manager) but I really don't want to have to abandon multi-core calculation. Has anyone else encountered this problem (I have not found anything via Google and stackoverflow), and do you know how to fix it?
The Graphics.drawLine call is synchronized on the Graphics object; if I additionally synchronized it on the BufferedImage, nothing changes.
If you want to see the bug for yourself, you can download the program at http://code.lucaswerkmeister.de/jfractalizer/. It is also available on GitHub (https://github.com/lucaswerkmeister/JFractalizer), but I only switched to GitHub recently, and in the first GitHub commit the problem is already apparent.
I think the problem is that neither BufferedImage nor Graphics is thread safe and that you see stale values in the thread that reads the BufferedImage after the computation.
Synchronizing on the BufferedImage like you said should actually help. But note that you must synchronize all accesses from all threads, including the read-only accesses. So my guess is that the thread that draws the BufferedImage on some component (which should be the AWT thread) does so without synchronization and therefore sees stale values.
However, I would suggest that instead of sharing a BufferedImage among multiple threads, you give each thread a separate image on which it can draw. Then, after all threads are finished, combine their work on a new image in the AWT thread.
Also, I suggest you use an ExecutorService for that if you don't do so already. It has the advantage that the visibility issues of the return values of the Callable tasks (in your case the image parts of the worker threads) are handled by the library classes.
If you combine these two approaches, you will not need to do any manual synchronization, which is always a good thing (as it's easy to get wrong).
Buffered images may not be thread safe because their data may live on the graphics card. However, this can be overridden. By using the ((DataBufferInt) image.getRaster().getDataBuffer()).getData() secret technique for high speed full image drawing (data buffer type depends on the image type you chose), you will get an unaccelerated image. As long as you never write to the same pixel twice, this should be theoretically completely safe. But remember to join() your threads somehow before reading pixels out of the image. join() method from thread not actually recommended as this requires thread death.
Related note:
The flicker you are witnessing is probably an artifact of the way awt renders to the screen. It runs in immediate mode, meaning every draw action you take immediately updates the screen. This slows down rendering of multiple objects directly to the window. You can get around the flicker by implementing a double buffering strategy. I like to draw to an intermediate image and then draw only that image to the screen.

Categories

Resources