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.
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.
Making a paint-like application that works by saving mouse points in an arraylist. My idea is to have a "points" arraylist with all the previously drawn stuff, and a "temp" arraylist to get and modify the current brush stroke the user just entered. This is necessary as the user can change the color and size, so my idea is to modify the current brush stroke based off what buttons have been pressed, then add that brush stroke to the rest of the picture. I searched around StackOverflow and found some code but cant get it to work how I want to (assuming I found the right code).
#Override
public void paintComponent(Graphics g1) {
super.paintComponent(g1);
final Graphics2D g = (Graphics2D)g1.create();
try {
g.setColor(brushColor);
for (Point point : tempArrayList){
g.fillOval(point.x, point.y, brushSize, brushSize);
}
} finally {
g.dispose();
}
The problem is that I need to clear the tempArrayList for the next brush stroke, which I can do when they change the color/size, but then it erases what was previously there. I am starting to think that I don't even need the "points" arraylist as descibed above because I was hoping that the g1 graphic would just save what the g graphic created.
I guess I just need to figure out how to add the g graphic to g1
Painting is controlled by the Swing API, there is a expectation that whenever paintComponent is called, you will repaint the entire state of the component (as part of your component might have been damaged due to some system event), so the short answer is, no, you can't...however....
You Could...
Paint to a BufferedImage instead and paint the BufferedImage when paintComponent is called
You Could...
Establish a series of "paintable" objects which contain information about what is to be painted and how it is to the painted, including the brush stroke, stroke color and fill color.
These would then be added to some kind of List and "painted" when the paintComponent method is called
Check out Custom Painting Approaches for exampled of painting from:
A List of objects
A BufferedImage.
The examples show how to draw Rectangles of different colours.
Hi I've been trying to get rid of my flickering on a JFrame app. Have searched around and seen that setDoubleBuffered(true) can be used for paintComponent of Jpanel, but not paint method of JFrame, nor applets.
Managed to diminish but not eliminate flickering by introducing the instructions this.createbufferstrategy(2) within paint method, and further reduced flickering with the instruction this.setignorerepaint(true) inside paint.
But I finally found an example of code that completely removes flickering and it works by drawing the static elements within the update function.
Tested fillRect within update in an applet and it works, but when copy pasted into a regular java application it does not work there within the jframe's update function.
Here's the code
Graphics graphics;
Image image;
public void update(Graphics g)
{
if (image == null) {
image = createImage(this.getWidth(), this.getHeight());
graphics = image.getGraphics(); }
graphics.setColor(Color.blue);
graphics.fillRect(0, 0, this.getWidth(), this.getHeight());
g.drawImage(image, 0, 0, this);//}
With an empty paint function this draws to the screen and fills it with blue in an applet, it also gets rid of flickering. But in a normal application of jframe it does nothing.
What needs to be done to allow either fillRect or drawImage to work from within update in a non applet regular application environment?
BTW, I'm a bit new to this if the graphics object is being modified itself by calling fillRect, how does that modify the image object? Because drawImage is necessary for the screen to turn blue.
PS I've tried not using both createbufferedstrategy and setignorerepaint, and nothing changes.
When using setIgnorePaint(true), its not allowing you to mess with the Graphics.
I'm guessing by the looks of your update(Graphics g) parameter, you call that within your paint method, using the paintMethod's graphic g to paint (inside paint method, you call update(g))
If you ignore paint, its not gonna allow you to use the paintComponent's Graphic parameter.
Post all code that includes graphics (Where you made your strategy, where you're calling this method, ect..)
The flickering is a pretty mainstream issue with the strategy, and I can't promise that I'll be able to fix it (my friend brought the problem up to me a while ago, but i didnt care enough to try and figure it out), but that should at least explain why your graphics aren't rendering
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.
is there a possibility to draw a JPanel on a specific location op a Graphics (or Graphics2D) object?
I override the paint method of my canvas, and call panel.paint(g) in there, but it does not work the way I woul hope.
#Override
public void paint(Graphics g){
Dimension size = panel.getPreferredSize();
panel.setBounds(pos.x, pos.y, size.width, size.height);
panel.paint(g);
}
the size object is correctly defined as I would wish, so that's not the problem. Also, the pos contains a correct x and y on the screen.
You should probably be using paintComponent instead of paint, since the latter is the AWT method, and the former is the Swing method.
One nice thing about Swing's paintComponent is that the Graphics passed is actually always going to be a Graphics2D, so you can:
Graphics2D g = (Graphics2D)lg;
Now you can use the getTransform to save the old transform, then modify the transform of the Graphics2D using either setTranform or the scale, translate and rotate methods. Don't forget to restore the old transform, or you'll likely fudge the next thing being drawn by that context.
I'll throw in that, depending on the circumstance, drawing to a BufferedImage might be appropriate. You can get a Graphics context using BufferedImage.getGraphics(). Then you can draw the BufferedImage's context by whatever means suit you.