Does repainting dirty region only improve performance?
If the answer is yes, how to do it?
Originally I use repaint() to call paintComponent(Graphics); however, recently I aware that repaint() is actually repaint(0,0,width,height), which repaints everything each time. If I use repaint() with parameters to specify the dirty region, in what way will it pass such data to paintComponent(Graphics)?
If I use repaint() with parameters to specify the dirty region, in what way will it pass such data to paintComponent(Graphics)?
The "clip bounds" or the Graphics object will be set to the specified region
1.Does repainting dirty region only improve performance?
Why? Do you have a painting problem. Don't micro optimize the code, unless you have a reason to do so. The code will be harder to maintain and debug. You will be adding extra logic to determine which regions need to be repainted.
Also, remember that multiple repaint requests get merged into a single request. So if you make a request to repaint the top/left corner of a component and the immediately request a repaint of the bottom/right, these two request will be merged into an area that includes both regions, which means the entire component will be repainted. So you have done extra work for nothing.
To repaint only "dirty" regions of a large JComponent you will need to use the RepaintManager. You can get the current RepaintManager with:
RepaintManager rm = RepaintManager.currentManager(component);
You can even replace the RepaintManager with your own custom version. Additional details can be found on Oracle's website.
Related
I wanted to access the Graphics class to draw a rectangle, but wanted to do so without having to call the paintComponent method. Is it possible?
Painting should always be done in a painting method.
The most common way is to do the custom painting in the paintComponent(...) method of the component.
Another approach is to "decorate" a component using the JLayer class and implement the painting in the paint(...) method of the JLayer. Read the section from the Swing tutorial on Decorating Components Using the JLayer Class for more information and examples.
Yes, it is possible, the same way it is possible to paint in a BufferedImage (by using straight the Graphics object by calling into the getGraphics() method).
BUT, unlike the case for BufferedImage, doing so for a component is likely to cause a mess. The reason is the underlying AWT/Swing code expects the component to paint itself, (see Component.paint or JComponent.paintComponent) while it (the underlying AWT/Swing code) will take care by about when this rendering needs to occur (e.g. scrolling, resizing, etc).
As such, if you paint from outside the component, at any time the AWT/Swing decides "Well, there's some new painting/repainting to be done", it will invoke the "standard methods", with the expectation your drawing code is there. As you paint it externally, if you don't make special arrangements to be notified of the "need repainting" situation, parts or the entire "drawing-from-outside" will be painted over or not painted at all or all kind of different messy situation.
In other words, if you paint-from-outside, you'll need to write not only the code that does the painting, but also:
re-implement heaps of code already implemented by the AWT/Swing which deals with the circumstances when the component needs to be (re)painted.
disable somehow the AWT/Swing "native" handling of painting.
Rest assured, you don't want that (even if you think you do).
PS. if you describe better why do you need that, you may get more useful suggestions on how to do it the proper way.
Swing normally combines multiple repaint() requests, in order to drive the paintComponent() method only once and thus enhance performance. Normally, this is what you want.
In my application, however, this is undesirable. My component is a large screen full of text (like a text editor). Each character includes attributes like foreground/background color and styles (like BOLD). Repainting the entire screen (which repaint(() does by default) is an expensive operation, and introduces unnecessary latency, when only updating a few disparate characters on-screen.
Therefore, I call repaint(x, y, width, height) multiple times, but only for those areas that have actually changed (typically, 50 characters or less, which may be scattered about the screen in two or three separate, contiguous areas).
The problem is, typically I will have characters change at the top and bottom of the screen. I call repaint for each of the affected areas, but Swing combines my multiple repaint(...) requests into a single repaint(...) request (this is default behavior) by computing the union of the areas to be repainted. This results in a single call to paintComponent() but with a large clipping rectangle (as returned by getClipBounds()) which encompasses all areas to be updated. Thus, I wind up repainting a large area of the screen, anyway (based on the ClipBounds), which is counter-productive and what I wish to avoid, since only a small portion of the screen has actually been modified and therefore needs to be repainted.
Question: Is there any way to mitigate this behavior, so that my paintComponent() method repaints only those areas that have been modified, and avoids unnecessary repainting of unmodified areas?
Additional detail, as requested:
The class is derived from JComponent. Basically, it's just an "empty" window on which I draw text characters using Swing's API. The window's maximum size is about 83 rows by 320 columns per row (this is on an Apple Cinema display), so for an 8x16 font, that's 2560x1328.
Basically, the application is a text editor (think: vi). The bottom line of the display is a Status Line. In a worst-case scenario, i might move the cursor one position to the right at the top of the window. This would cause the Status Line at the bottom of the window to be updated to reflect the new cursor position (row, column). So, we have a few locations changing at the top of the window, and a few locations changing at the bottom of the window. I would issue 2 repaint(...) requests, one for each modified area of the window. However, repaint() will combine those two requests, and call paintComponent() once with a bounding rectangle (which defines the area to be repainted) that is the union of the two areas that were actually updated. The resultant rectangle will be very large, extending from the top of the window to the bottom. Thus, in paintComponent(), I wind up repainting a large part of the screen, even thought the vast majority of it was not modified. That is what I'm trying to avoid.
Is there any way to mitigate this behavior, so that my paintComponent() method repaints only those areas that have been modified,
You could try using the paintImmediately(...) method of the JComponent class.
This will result in multiple smaller paint requests being done right away without the benefit of the RepaintManager. Only you can decide is multiple smaller requests performs better than a single larger paint request.
I'm looking at the documentation for javax.swing.JComponent, and this method stuck out to me because I am currently trying to create a subclass of this type. Is there any point in overriding the getInsets()? What is the purpose of this method when we already have getPreferredSize()?
getInsets defines space which can be taken up by extra content, like borders, which is, generally, added to the preferredSize and offsets the position (translates) of the Graphics context, to ensure that the actual content is painted instead the insets.
Personally, unless you intend to prevent anybody from modifying the state of the components borders or are thinking for providing margins of some sort, I'd leave it alone (and not override it)
Suppose my custom rendering takes long time. By what means can I render cells in background? Probably I need to receive event when some cell becomes visible, then render it in separate thread, then actually paint.
How to accomplish this?
UPDATE
I know the render should be fast. But it does not in my case. So, I need extra layer between presentation and model, which will contain some sort of cache. For example, images of prerendered cells. The question is about how to hook this layer to the object.
A render should be as fast as possible. A render should't do complex computation, just paint the model, not calculate it every time.
Renders are called from EDT (Event Dispatch Thread), and I think, without more information, that maybe you should calculate the model in a separate thread, and the render just paint the model in the EDT.
If the model is still not available, you could disable the component for example.
Post the code for more precise solution please.
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.