Swing rendering... does it always have to be repaint() and paintComponent()? - java

In my program I am using Swing windows for my GUI, and I know that I'm supposed to use repaint and paintComponent methods to render swing components. I also have several custom class objects that need to be rendered inside the main window. For the sake of understanding, my program is a game that will have moving objects that need to be rendered 60 times per second.
In a game development tutorial I watched, the guy used a Jframe with Canvas, but he didn't use the paintComponent method. He simply made his own render() methods to draw all the graphics using graphics context he obtained from creating a bufferStrategy.
So if that works (which it does), why does everyone say to use the paintComponent methods and what exactly is the difference between them?
If I were to use the paintComponent way of doing things, how would I use bufferStrategy with that?

In a game development tutorial I watched, the guy used a Jframe with Canvas, but he didn't use the paintComponent method. He simply made his own render() methods to draw all the graphics using graphics context he obtained from creating a bufferStrategy.
Canvas is an AWT component, using a BufferStrategy, you take over the painting process and become responsible for update the Graphics context and scheduling it's push to the hardware/screen.
This is commonly known as "active painting", as you are constantly (one would assume at a constant frame rate), updating the buffer.
Swing uses a passive paint process, so you never know when a paint process might occur. Swing has it's own mechanisms for determining what should be repainted and when. Using repaint, you make a request to these mechanisms that your component be repainted, but there is no guarantee that a paint cycle will be initiated because of it.
If you use any Swing component for you painting, you MUST use repaint and paintComponent, as the Swing is responsible for providing the context onto which you can paint.
If you want to use Canvas, then you can use a BufferStrategy instead.
Essentially, they are different approaches to painting. Remember though, if you use a BufferStrategy, you lose ALL of the Swing API, you CAN NOT use Swing components with this approach, as they are not designed to work this way...

Related

Multiple 2D Graphics Drawing Functions in Java

The program I am working on includes a class named GameForm that extends JFrame. This form is going to contain a map (just a series of rectangles), as well as certain objects on the map.
However, I would not be able to draw all of these objects with a single paintComponent(Graphics g) function, since not all objects in the game always have to be drawn at the same time. For example, the drawMap() function would only be called when the form first loads, whereas all other drawing functions would be called after each turn.
However, from what I have read (and please correct me if I am wrong), only one paintComponent function is allowed in the class, and other functions cannot make use of its Graphics2D object.
Are there any ideas as to how this can be implemented?
People who are new to Swing / GUI programming often imagine JFrames to be like a draw surface or paper. However, you will have to get used to the fact that this is not the case.
First of all, a GUI program has some kind of EDT (Event Dispatch Thread). This thread is where all the GUI action happens. Updating the GUI and responding to user input happens here. This is necessary because user interaction and programmatic changes to the GUI need to be synchronized well.
Back to the topic, a JFrame is basically just a rectangle that is registered to the System to be your "draw surface". Rather than just painting on top of it, you are asked to paint it.
That's what paintComponent(Graphics) is good for. Obviously, you don't want to paint all the time. It just works like:
user opens your window
system tells your app: "hey, you wanted this surface, please paint it"
the Graphics from paintComponent() is used to repaint your frame (quickly)
your application remains inactive until the user makes the next input
If you want to animate your frame, you have to work like this:
tell the system: "hey, I'd like to repaint my surface" (calling repaint())
system calls paintComponent() and you repaint your stuff
the next call must be delayed
start over, paint the next image
Note that the delay is important because all of this happens on the holy EDT. The EDT handles everything and needs to "breathe" so the user can do stuff while you're doing your animation.
All in all, what you've learned is:
Save all the state you need for painting in variables.
When paintComponent() is called, draw onto the surface
If you want to animate, call repaint() -> paintComponent() will be called
never block the EDT
Last thing to consider: don't use JFrame to paint directly to it.
Rather than that, add a JPanel to the frame and override its paintComponent() method.
Generally speaking, what you wish to achieve can be done in a couple of ways. It's strictly related to so-called sprites (http://en.wikipedia.org/wiki/Sprite_%28computer_graphics%29) and image buffering (http://en.wikipedia.org/wiki/Multiple_buffering). Simplest approaches would be:
a) in paintComponent() of a JPanel added to your JFrame generate the resulting image by processing all the input data/user events/machine state,
b) you can prepare & store the overlay as e.g. BufferedImage, updating it as needed, and then paint it over your JFrame during a single call - the state of JFrame will be updated only on paint events (paint(), paintComponents() etc, so you must force invalidation by hand if the map changes without direct JFrame interaction (resizing the window, covering it with other frames etc), e.g. by calling repaint() etc.
c) you can get the drawing context by calling getGraphics() (http://docs.oracle.com/javase/7/docs/api/javax/swing/JFrame.html#getGraphics%28%29), and then using the returned object (probably casted to Graphics2D) as your canvas whenever the need arises. Note that this is actually the worst solution in terms of efficiency.
They ain't the only ones possible - I, for once, use OpenGL/JOGL for most of my 2D rendering needs, since it allows insane rendering speed with all the profits of 3D graphics [interpolation, scaling, rotations, alpha-blending, perspective, geometry morphing, shading etc] with only minimal functional overhead.
Also, note it is usually advisable to draw on a dedicated canvas component (e.g. JPanel) instead of global JFrame - it's connected to so-called lightweight vs heavyweight component difference and other OOP/Swing/AWT/EDT concerns; it also allows to hide the map and reuse the JFrame for something else with one simple JPanel#setVisible(false) call.
See java what is heavier: Canvas or paintComponent()? for more information.

Flickering when updating overlapping JPanels inside a JLayeredPane using timestep

I am making a game in Java. Basically, I have two different "planes" of updating that I need to take care of the. The base layer is the actual game painting itself. It is simply a JPanel that covers the entire JFrame, and is drawn to using its Graphics object.
I use a fixed timestep to take care of these first graphical updates. I have overwritten the paintComponent() method to do absolutely nothing, as I have written a custom render(float interpolation) method that takes care of that, as to prevent unwanted updates.
However, this panel can take no input beyond primitive mouse clicks and keyboard input. I need the ability to create various menus, text boxes, etc, that are also on the screen. Like various abilities, or even the "Menu" button that usually appears in the upper left corner of most games.
To take care of that input, such as creating buttons, I have a second JPanel that has setOpaque(false) applied to it. Then I create various Swing components that I might need, such as a JButton.
To contain the two JPanels, I use a JLayeredPane, and set their layers appropriately, as seen below. This way the input layer should always be on top of the actual game layer.
The code below shows how I create and add the Swing components to each other. addLoginDialog() is a method that adds a Swing component for the login. It has been tested and works properly, and isn't the problem.
private void initComponents()
{
//This code is inside of the JFrame
wholePane = new JLayeredPane();
add(wholePane);
guiPanel = new GUIPanel();
guiPanel.setOpaque(false);
gamePanel = new RPGPanel();
gamePanel.setOpaque(false);
wholePane.add(gamePanel, JLayeredPane.DEFAULT_LAYER);
wholePane.add(guiPanel, JLayeredPane.POPUP_LAYER);
guiPanel.addLoginDialog();
}
So when I run the code, I get horrible flickering. This is the code that is run from my fixed timestep ~60 times per second.
public void handleRepaint()
{
//I don't use repaint() for the game drawing so I can be sure that fps is controlled.
Graphics g = gamePanel.getGraphics();
gamePanel.render(g);
g.dispose();
wholePane.repaint();
}
The problem is, I think, that the two different systems of updating the screen are clashing. The standard paintComponent() system is great for more static screens, but when I need to update consistently and keep track of the fps, I can't have updates going off randomly.
However, for the input layer, I only want to update as Swing normally does. When the mouse moves over a button, when I component is moved or is resized, etc.
Also, note the way the screen flickers: The Background image goes blank and then comes back again repeatedly. The input panel is always there, but is actually painted behind the game drawing, which shouldn't happen, because it is put in the default layer. The reason I know it isn't completely disappearing is because the game painting is partially transparent, so I can see underneath it, and the buttons I added are still there.
My main question is, how can I stop the flickering? Why is the game drawing being drawn on top of the input components when the game drawing is being done on the Panel that is in a lower layer in the JLayeredPane? And I supposed most importantly, what is causing the flickering? Thank you for any help.
Why is the game drawing being drawn on top of the input components
when the game drawing is being done on the Panel that is in a lower
layer in the JLayeredPane?
Mostly because you've circumvented how Swing works.
Let's start with the fact that the Graphics context is a shared resource (typically there is a single Graphics context per native peer), that is, every component gets the same context, this means, when you use your own painting routine, you are basically painting over every thing else.
The RepaintManager is responsible for making decisions about what and when something should be painted. So what you now have is two artist fighting over the same canvas, wanting to paint on it simultaneously, which is just making a mess.
Add into the fray that Swing's painting process is not thread safe and you end up with a complete mess.
If you absolutely must have control, then you need to remove Swing's painting engine from the equation.
Start by taking a look at Passive vs. Active Rendering
ps- There is also hard ware acceleration benefits to using a BufferStrategy

Dedicated drawing surface in Java like Android's SurfaceView?

I am looking for a dedicated drawing surface in Java to draw 2D very fast in succession, very much like Android's SurfaceView. I have been using JPanels but I am not sure about the performance and efficiency when calling repaint() multiple times. The documentation says that:
If this component is a lightweight component, this method causes a call to this component's paint method as soon as possible. Otherwise, this method causes a call to this component's update method as soon as possible.
Note the as soon as possible. I want instantaneous. Using the SurfaceView in Android I had full control of the Canvas and could draw when I wanted.
Should I be using for instance the Canvas from AWT or JComponent from Swing, or something else? Note that I want a dedicated drawing area and I do not care about other compontents. I would like to register mouse events though. And again, I want the full control to draw on the area without having to call repaint or some other method, like I do with SurfaceView on another Thread.
I asked a similar question but now I feel like I can be more specific thanks to the answers from that question.

What is the use of Canvas in AWT?

I've been searching many sources yet I still cannot find a decent explanation. Why should I use it, what is its' purpose and why does it differ from JPanels and such?
The speciality of Canvas is that, like Window, it can provide customized hardware-accelerated double-buffering and page-flipping. See BufferStrategy.
A canvas is for drawing on, basically. It also serves like a Panel for creating a custom AWT-based component, but unlike Panel it can't contain other components.
From my understanding, Canvas is just the AWT version of Swing's JComponent. You shouldn't use it directly, unless you're making a pure AWT app. You can find more info here.
public class Canvas
extends Component
implements Accessible
A Canvas component represents a blank rectangular area of the screen onto which the application can draw or from which the application can trap input events from the user.
An application must subclass the Canvas class in order to get useful functionality such as creating a custom component. The paint method must be overridden in order to perform custom graphics on the canvas.
A canvas is for drawing on, basically. It also serves like a Panel for creating a custom AWT-based component, but unlike Panel it can't contain other components.

Simplest way to draw primitives in Java (not OpenGL)

Just started to getting familiar with graphics in Java.
What is the simplest way to draw primitives (lines, rectangles) in Java without OpenGL (JOGL)?
Looking for methods like putPixel, lineTo, etc.
UPD.
Where to paint? Canvas? Panel? Bitmap?
The built in interface is called "Graphics2D".
Here's a link to a Java Graphics2D Tutorial: http://java.sun.com/docs/books/tutorial/2d/index.html
You can get a Graphics/Graphics2D instance from any AWT/Swing component via the paint method. JPanel is probably best since it fits well with swing and is lightweight, meaning that only one native window is created - for the top level window. Swing components can also be double-buffered, meaning that the painting is done to an offscreen buffer first, before being transferred to the screen. This gives a smoother appearance and avoids flickering that can happen when painting directly to the screen, and is particularly important for smooth animation.
You can specifically draw to an offscreen buffer ("bitmap") that you can use afterwards, e.g. to draw an image for saving as a file later:
BufferedImage offImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Grapics2D g2 = offImg.createGraphics();
// .. optionally set attributes ..
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
My recent question about horizontal scrolling in Java includes a tiny little graphics example source code that you could use as a base to work from. There are both AWT and Swing implementations. The AWT doesn't support horizontal scrolling, so I'll be using swing.
Not recommending these as best practice or anything, they were a quick demonstration of my particular issue, but it might be enough to get you started.
Link is How to use my trackpad for horizontal mousewheel scrolling in a Java AWT ScrollPane
The original Java user interface classes are called AWT. These were "heavyweight" components, that sometimes acted differently on different systems (Windows, Mac, Unix). These components were difficult to use to make a GUI.
Sun developed Swing, which is a set of "lightweight" components that, to the maximum degree possible, work the same on different systems. These components made GUI development somewhat easier.
In order to have a canvas for graphics, you start with a javax.swing.JFrame. You add a child javax.swing.JPanel to the JFrame. You draw on the JPanel by overriding the paint method.
The JPanel paint method takes a java.awt.Graphics as input. You can cast Graphics to java.awt.Graphics2D. The methods of Graphics2D allow you to draw rectangles, images, text, lines, and arbitrary polygons.
You can find out more about Swing by reading Sun's Creating a GUI with JFC/Swing tutorial. You can find out more about 2D Graphics by reading Sun's 2D Graphics tutorial. More details on the Java classes I've mentioned can be found in the Javadoc.

Categories