Java Paint Method Being Called By Graphics2D - java

I was working on creating a fade-in animation and I ran into a problem where whenever I try to draw a BufferedImage with:
g2d.drawImage(unknown.getScaledInstance(100, 200, BufferedImage.SCALE_SMOOTH),
10,button.getLocation().y - 200, this);
after using
.setCompositeAlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity)
My JPanel's paint method was constantly being repeatedly and continuously being called. I tried commenting out the drawImage bit and everything worked fine again. I also noticed that when I called the paint method from another class using:
answerPanel.paint(answerPanel.getGraphics());
The paint method stopped being continuously called.
So could anyone shed some light on why the drawImage line is causing the paintMethod to be constantly called until I call it from another class? Thanks!

Agree with #user2864740, you should never call paint(...) directly, but I don't think that is the root of the problem here.
The last parameter to drawImage(...) (e.g. this) is an ImageObserver. It receives callbacks whenever an Image is updated (changed), and will cause a repaint() (which will eventually call paint()).
When you invoke Image.getScaledInstance(...) this will immediately return a new Image instance, but the actual scaling (or, resampling, really) will happen later. The image observer will handle "automagic" repaints of your component, as soon as (parts of) the scaled image is done. You component will be requested to repaint one or more times.
You should be able to observe the effects of this, by passing null as the last parameter, instead of this (probably, nothing will be painted, but you'll avoid the repaint loop).
The reason your component will go into a repaint loop, is because each time the component is asked to repaint itself, a new scaled instance is returned, causing a restart of the scaling and the automatic repainting by the image observer, as described above.
To fix the problem, don't use getScaledInstance() and instead, try:
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.drawImage(unknown, 10, button.getLocation().y - 200, 100, 200, this);
Another way, would be to make sure you create the rescaled image only once, and continue using it for each repaint:
Image scaled;
public void paint(Graphics g) {
...
if (scaled == null) {
scaled = unknown.getScaledInstacnce(100, 200);
}
...
g2d.drawImage(scaled, 10, button.getLocation().y - 200, this);
}

Related

Why is my gif animation in paintComponent not working?

I am creating a Java game, and in this, I have a plane that can be moved around by the user, however if the plane hits any of the sides, it should explode - meaning there should be an explosion at that location. I used links such as: Why gif animation doesn't animate when using it in paintComponent()? and other ones to find out how to animate a gif, however, my code doesn't work.
Here is the relevant code:
ImageIcon ii = new ImageIcon(this.getClass().getResource("explosion.gif"));
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(ii.getImage(), planeX, planeY, this);
System.out.println("drawn");
Although "drawn" is being printed out on the console, the explosion gif doesn't load. I checked the file name and it was correct. However, I did use the same gif somewhere else in my code and it worked there, so is there something that prevents having a gif being used twice in Java?
Here is my entire paintComponent() method:
super.paintComponent(g);
setFocusable(true);
requestFocus();
background = new ImageIcon("bg.png").getImage();
g.drawImage(background, -6, 0, 700, 700, null); //background of JPanel
if (!hit) g.drawImage(plane, planeX, planeY, planeLen, planeHei, null); //only draw plane if not exploded
else if (count == 0)
{
ImageIcon ii = new ImageIcon(this.getClass().getResource("explosion.gif"));
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(ii.getImage(), planeX, planeY, this); //doesn't work
System.out.println("drawn"); //printed
}
title = new ImageIcon("title.png").getImage(); //transparent, is drawn
g.drawImage(title, -9, 20, 700, 218, null);
title2 = new ImageIcon("title2.png").getImage(); //transparent, is drawn
g.drawImage(title2, 10, 245, 670, 20, null);
I have checked title and title2 and they are transparent, meaning that the explosion should be seen. I even tried it below those two images, and the explosion still is not visible. I am using JButtons and KeyListeners on this panel as well.
Please let me know if I should add anything else, and thank you in advance!
You should always pass this as the final argument to drawImage in a painting method. If an image has multiple frames, or the image is loaded in the background, this tells the component to paint itself again as more of the image becomes available.
Painting of components happens frequently. It is controlled by the system, not just by code calling the component’s repaint methods. In fact, it’s common for painting to happen many times per second. Many system events beyond a programmer’s control can trigger painting, such as a user moving a window, or another window moving, or even the user moving the mouse.
For this reason, painting should not perform slow operations like loading files. Move the loading of your images out of your painting method, and into the class’s constructor. Store the Image objects in private fields, where paintComponent can use them again and again without reloading them for every painting run.
Since the timing of painting is beyond your control, it is important that you never change the state of a component in a painting method. Move setFocusable and requestFocus out of your paintComponent method. Those calls should be in the code that builds your component and adds it to a window.
Finally, be aware that loading images from files will not work if you ever package your application in a .jar file. A .jar file is a single compressed archive; the entries in it are not files anymore, they’re just sequences of bytes inside the archive. Loading all files using getResource will address this, since getResource looks in every .jar file in the classpath.
Never mind, I had to make a duplicate of "explosion.gif". It works fine now!
However, please let me know if there is another way of doing this, much faster AND efficiently. Thanks!

Drawing Strings over PaintedImage Java

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.

fillRect or drawImage not activating within update method in java

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

Java; paint ( Graphics ) called too much after resizing window!

I have a question. Recently I was looking into ways to implement hardware rendering using Java. My goal was not to use an external library such as OpenGL. I found a post on a website that detailed how to do so.
This is what the code was (I renamed some items):
#Override
public void paint(Graphics g) {
createVolatileImage();
do {
GraphicsConfiguration gc = getGraphicsConfiguration();
Graphics offscreenGraphics = volatileImage.getGraphics();
int validationCode = volatileImage.validate(gc);
if (validationCode == VolatileImage.IMAGE_INCOMPATIBLE) {
createVolatileImage();
}
offscreenGraphics.setColor(getBackground());
offscreenGraphics.fillRect(0, 0, getSize().width, getSize().height);
offscreenGraphics.setColor(getForeground());
paint(offscreenGraphics);
g.drawImage(volatileImage, 0, 0, this);
} while (volatileImage.contentsLost());
}
private void createVolatileImage() {
GraphicsConfiguration gc = getGraphicsConfiguration();
volatileImage = gc.createCompatibleVolatileImage(getWidth(), getHeight());
}
Unfortunately, if I resize the window - the paint ( Graphics ) method (in the class Canvas) gets called like 1,000 times within a second, causing an OutOfMemoryException.
Has anyone encountered this before?
Thanks a lot in advance!
The reason you are getting an OutOfMemoryException is because you never clean up your VolatileImage. The way I see it, you are allocating a new VolatileImage every time paint() is called, which can happen many hundreds (or in your case over a thousand) times per second. Unless you free the memory used by the VolatileImage or fix things so that you make the allocation once instead of once per frame, your application's memory space will balloon until you crash the JVM. Try adding a call to offscreengraphics.dispose() at the end of your rendering loop. Also read the Javadoc.
EDIT:
Another useful reference.
I really don't think you should have a loop in your paint method. It should paint a single time for each action that needs a paint, then return.When you drag the screen, the OS/env will take care of dispatching repaint messages over and over to your app. You should repaint if possible then just return. No loops checking if content is lost, the OS will tell you when to do your painting.
As discussed in Painting in AWT and Swing, this is the expected behavior when painting in AWT. In particular, a system-triggered painting operation occurs when a component is resized. As #rjacks notes, you need to dispose() any resources created in paint().

Java 2D Graphics Rectangles

I'm attempting to make a Java Applet that will allow me to draw a graph data structure in a canvas. I will do this by clicking where I want to create nodes, and clicking the nodes to connect them. The problem is I cannot get the paint() method to behave correctly. I add new nodes to the graph (and squares on the canvas) inside the mousePressed(MouseEvent e) method using,
Graphics g = this.getGraphics();
g.setColor(Color.blue);
g.fillRect(e.getX(), e.gety(), 40, 40);
Everything works fine, until I resize the window, and then all the filled rectangles vanish. I overrided the paint method to just an empty method, but the same thing still happens. I can't add the fillRect commands inside paint() because I don't know what rectangles exist until the user interacts with it using the mouse.
How can I use g.fillRect() inside the mouse listener methods and make them stick?
The problem is the place you're drawing to isn't persistant. At any moment, you can lose everything you've drawn to it. The paint(Graphics) method is called when this happens. You'll either need to repaint the entire picture every time this happens, or you'll need to set aside a canvas to draw to and copy the contents to your applet's Graphics as needed.
Here's how to create and draw to an image:
http://java.sun.com/docs/books/tutorial/2d/images/drawonimage.html
Then, in your paint method, use your Graphics' drawImage(...) method to display the image you've created.
I don't know if I'm reading this correctly, but why not just store the location of the last click in a variable to be painted later, when the paint() method is called?
You've got to override the window resize action listener and call repaint inside of it.
The Graphics is temporary. When a region gets dirty, it will be repainted.
The best way is to create a BufferedImage, paint to it on mousePressed and call repaint.
When paint is called, draw the Image onto the passed Graphics Object. This way you don't need to store the Rectangles and you got a buffer which will improve performance.

Categories

Resources