Can't stop flickering on JPanel - java

I have class that creates a new thread.
`
public ScreenG(JPanel PanelR)
{
Panel = PanelR;
RenderImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
FPS = 25;
Hide = false;
(new Thread()
{
public void run()
{
while(true)
{
if(Hide == false)
{
Timer = System.currentTimeMillis() + (1000/FPS);
if(!DrawRendering)
{
Graphics g = Panel.getGraphics();
g.drawImage(RenderImage, 0, 0, null);
}
DrawRendering = false;
while(System.currentTimeMillis() <= Timer) try { Thread.sleep(1); } catch (InterruptedException e) {Thread.currentThread().interrupt();}
}
}
}
}).start();
}
public void draw(BufferedImage ImageR)
{
DrawRendering = true;
RenderImage = ImageR;
Graphics g = Panel.getGraphics();
g.drawImage(RenderImage, 0, 0, null);
}`
In my main I create a new instance of ScreenG. This will start a new thread that draws a bufferedImage onto a JPanel with a consistent FPS.
In the main I would then call draw with the image that I created. Sometimes it works but sometimes the image on the panel flickers. I try variations like the draw function taking over the drawing. Non of them work. I could only REDUCE the flickering.

Not possible by design. Swing does not synchronize to the bitmap raster DMA that's actually sending the screen data to your monitor, so it always possible that the screen buffer is read by the DMA while you're busy rendering to it (possible exception is Fullscreen mode).
To at least minimize flickering follow the recommended method of custom Swing painting: https://docs.oracle.com/javase/tutorial/uiswing/painting/
You can easily trigger periodic repaints on the EDT using a Swing timer, or SwingUtilities.invokeAndWait/invokeLater from another thread (whatever works best in your design).

The flickering can be because the rendering isn't fast enough from an update.
Now I do recommend you use Swings paintComponent (Graphics g) when rendering its components. That being said. To solve the flickering for you add a BufferStrategy in your JFrame
Without that code avaible I can only provide a general solution.
JFrame jframe = new JFrame ();
...
BufferStrategy bufferstrategy = jframe.getBufferStrategy ();
if (bufferstrategy == null) {
jframe.createBufferStrategy(3);
return;
}
g.dispose();
bufferstrategy.show();
To read more about BufferStrategy I recommend a read over at the documentation.
Small Note
There is no reason in your code to either store JPanel PanelR or BufferedImage ImageR. You can instead directly invoke methods directly on PanelR resp. ImageR.

Thank you for the answers. I read Oracle tutorial that you recommended and get my paintComponent() function working correctly on the main thread. To do that I am calling JPanel().repaint() from the draw() function. I will learn about using BufferStrategy next.

Related

Can't Draw Graphics Because Of "#Override" Annotation

I have been experimenting with drawing on a JFrame, so I could use these experiments in the future for a program I might create. However, I have found a problem that I am not able to solve:
How to draw stuff while having a timer set up.
public static void MyTimer() {
JFrame frame = new JFrame("Colors");
int width = 700;
int height = 700;
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setBackground(Color.BLACK);
frame.pack();
frame.setSize(width, height);
frame.setVisible(true);
frame.setResizable(false);
TimerTask task;
task = new TimerTask() {
int a = 2;
#Override
public void run(Graphics g) {
g.drawRect(a, 2, a + 66, 68);
g.fillRect(a, 2, a + 66, 68);
a = a + 20;
}
};
timer.schedule(task, 0, 1000);
}
As you can see, I am trying to draw a new square every second. The problem is, I get an error in the code:
method does not override or implement a method from a supertype
How can I fix this?
How can I fix this?
The run() method does not take a parameter. Get rid of the Graphics parameter. That will get rid of the compile error.
However, that still will not help with your painting.
Instead you need to override the paintComponent(...) method of a JPanel then you add the panel to the frame. Then you use the Graphics object passed to the paintComponent() method to do your painting.
Read the section from the Swing tutorial on Custom Painting for more information and working examples to get you started.
Also you should NOT be using a TimerTask for animation. You should be using a Swing Timer. Then in the actionPerformed(...) method of the ActionListener you would change the properties of your custom painting (ie in your case add a new square object to be painted) and then invoke repaint() on the panel.
, I am trying to draw a new square every second
Check out the DrawOnImage example found in Custom Painting Approaches. It will show you how to add a Rectangle object to a BufferedImage.

How to create an animated Image from still frames?

Given a list of true-color full frames in BufferedImage and a list of frame durations, how can I create an Image losslessly, that when put on a JLabel, will animate?
From what I can find, I could create an ImageWriter wrapping a ByteArrayOutputStream, write IIOImage frames to it, then Toolkit.getDefaultToolkit().createImage the stream into a ToolkitImage.
There are two problems with this attempt.
ImageWriter can only be instantiated with one of the known image encoders, and there is none for a lossless true-color animated image format (e.g. MNG),
It encodes (compresses) the image, then decompresses it again, becoming an unnecessary performance hazard.
[Edit]
Some more concise constraints and requirements. Please don't come up with anything that bends these rules.
What I don't want:
Making an animation thread and painting/updating each frame of the animation myself,
Using any kind of 3rd party library,
Borrowing any external process, for example a web browser,
Display it in some kind of video player object or 3D-accelerated scene (OpenGL/etc),
Work directly with classes from the sun.* packages
What I do want:
Frame size can be as large as monitor size. Please don't worry about performance. I'll worry about that. You'll just worry about correctness.
Frames all have the same size,
an Image subclass. I should be able to draw the image like g.drawImage(ani, 0, 0, this) and it would animate, or wrap it in an ImageIcon and display it on a JLabel/JButton/etc and it would animate,
Each frame can each have a different delay, from 10ms up to a second,
Animation can loop or can end, and this is defined once per animation (just like GIF),
I can use anything packaged with Oracle Java 8 (e.g. JavaFX),
Whatever happens, it should integrate with SWING
Optional:
Frames can have transparency. If needed, I can opaquify my images beforehand as the animation will be shown on a known background (single color) anyway.
I don't care if I have to subclass Image myself and add an animation thread in there that will cooperate with the ImageObserver, or write my own InputStreamImageSource, but I don't know how.
If I can somehow display a JavaFX scene with some HTML and CSS code that animates my images, then that's fine too. BUT as long as it's all encapsulated in a single SWING-compatible object that I can pass around.
You're right that ImageIO isn't an option, as the only animated format for which support is guaranteed is GIF.
You say you don't want to make an animation thread, but what about a JavaFX Animation object, like a Timeline?
public JComponent createAnimationComponent(List<BufferedImage> images,
List<Long> durations) {
Objects.requireNonNull(images, "Image list cannot be null");
Objects.requireNonNull(durations, "Duration list cannot be null");
if (new ArrayList<Object>(images).contains(null)) {
throw new IllegalArgumentException("Null image not permitted");
}
if (new ArrayList<Object>(durations).contains(null)) {
throw new IllegalArgumentException("Null duration not permitted");
}
int count = images.size();
if (count != durations.size()) {
throw new IllegalArgumentException(
"Lists must have the same number of elements");
}
ImageView view = new ImageView();
ObjectProperty<Image> imageProperty = view.imageProperty();
Rectangle imageSize = new Rectangle();
KeyFrame[] frames = new KeyFrame[count];
long time = 0;
for (int i = 0; i < count; i++) {
Duration duration = Duration.millis(time);
time += durations.get(i);
BufferedImage bufImg = images.get(i);
imageSize.add(bufImg.getWidth(), bufImg.getHeight());
Image image = SwingFXUtils.toFXImage(bufImg, null);
KeyValue imageValue = new KeyValue(imageProperty, image,
Interpolator.DISCRETE);
frames[i] = new KeyFrame(duration, imageValue);
}
Timeline timeline = new Timeline(frames);
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
JFXPanel panel = new JFXPanel();
panel.setScene(new Scene(new Group(view)));
panel.setPreferredSize(imageSize.getSize());
return panel;
}
(I don't know why it's necessary to set the JFXPanel's preferred size explicitly, but it is. Probably a bug.)
Note that, like all JavaFX code, it has to be run in the JavaFX Application Thread. If you're using it from a Swing application, you can do something like this:
public JComponent createAnimationComponentFromAWTThread(
final List<BufferedImage> images,
final List<Long> durations)
throws InterruptedException {
final JComponent[] componentHolder = { null };
Platform.runLater(new Runnable() {
#Override
public void run() {
synchronized (componentHolder) {
componentHolder[0] =
createAnimationComponent(images, durations);
componentHolder.notifyAll();
}
}
});
synchronized (componentHolder) {
while (componentHolder[0] == null) {
componentHolder.wait();
}
return componentHolder[0];
}
}
But that's still not quite enough. You first have to initialize JavaFX by calling Application.launch, either with an explicit method call, or implicitly by specifying your Application subclass as the main class.
something like
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(getImageForCurrentTime(), 3, 4, this);
}

How do I properly paint to a frame/panel?

I've tried many variations of everything at the bottom of this class and so far nothing works. Occasionally an edit will cause the print statement to work, but the window just always opens at a size that isn't even the one I set it up and stays blank. I don't know what's wrong with it. I'm trying to print 1024 rectangles on my window with a pause inbetween each print. The values are right, they're just not getting painted for some reason. Changing the method to paintComponent doesn't seem to do much either. The code is long, so here's a pastebin: http://pastebin.com/ridipz3X. The important stuff is at the end though:
JFrame frm = new TestEnvironment();
frm.setSize(1152, 1152);
frm.setVisible(true);
JPanel panel = new JPanel();
frm.add(panel);
t = 0;
i = 0;
while (t < x - 1) {
panel.repaint();
j++;
t++;
Thread.sleep(10000);
}
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(getForeground());
try {
for (int h = 0; h < 1152; h++) {
g.drawRect(h, 0, (int) (((ampArray[h][j]) / maxFreq) * 1152),
1);
g.fillRect(h, 0, (int) (((ampArray[h][j]) / maxFreq) * 1152),
1);
System.out.println(ampArray[h][j]);
}
} finally {
g.dispose();
}
}
}
Thanks
Painting is typically done from within the paintComponent method of a component that extends from JComponent, typically a JPanel, depending on your needs.
You should refrain from overriding paint of top level containers like JFrame for a number of reasons, including, they are not double buffered, you will end up painting under the window decorations, Swing windows contain a number of layered components which make up the viewable content of a window, meaning that you will either paint over or under this content, which just gets messy.
You should never dispose of Graphics context that you did not create yourself.
Swing is a single threaded environment, that is, anything that blocks the thread (such as Thread.sleep, will prevent it from process new repaint requests and events, making it look like you program has stopped.
Swing is also not thread safe. This means that you are required to ensure that all updates and interactions with the UI are done from within the context of the Event Dispatching Thread.
Animation is typically achieved through the use of a javax.swing.Timer or SwingWorker depending on the complexity of the animation. You can use a Thread, but it complicates the issues as you will be required to ensure that all updates to the UI (directly or otherwise) are done from within the context of the EDT manually.
Take a look at:
Performing Custom Painting
Painting in AWT and Swing
Concurrency in Swing

Java .drawImage : How do I "unDraw" or delete a image?

I need a certain image to be redrawn at different locations constantly as the program runs. So I set up a while loop that should move an image across the screen, but it just redraws the image on top of itself over and over again. What am I doing wrong? Is there a way to delete the old image before drawing it in a new location?
JFrame frame = buildFrame();
final BufferedImage image = ImageIO.read(new File("BeachRoad_double_size.png"));
JPanel pane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int num = 0;
boolean fluff = true;
while (fluff == true) {
num = num + 1;
g.drawImage(image, num, 0, null);
if (num == 105) {
fluff = false;
}
}
}
};
frame.add(pane);
You can't code a loop in the paintComponent() method. The code will execute so fast that the image will only be painted in the final position, which in your case should be with an x position of 105.
Instead you need to use a Swing Timer to schedule the animation every 100 milliseconds or so. Then when the timer fires you update the x position and invoke repaint() on the panel. Read the Swing tutorial on Using Swing Timers for more information.
Putting a while loop inside a paintComponent method is not the way to do it. Instead, there should be some setup like the following:
...
final int num = 0;
final JPanel pane;
Timer timer = new Timer(10, new ActionListener() {
public void actionPerformed(ActionEvent e) {
num++;
pane.repaint();
}
});
pane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, num, 0, null);
}
});
timer.start();
This will move the image ever 10 milliseconds, as specified in the Timer constructor.
This is a common issue people starting out in animation have, as I did. You can't 'remove an image' from the screen. However, you can repaint the entire screen, then redraw your image at a new location.
In psuedocode:
while (condition)
background(white); //or whatever color your background is
drawImage(x,y);
The code above clears the screen so it's safe for you to redraw your image. This effectively 'deletes' your image.
Edit: I didn't read your code, I just addressed your question. So other answers that fix your code are probably better than mine.

How does JButton implement its icons?

I'm trying to understand how Swings JButton/AbstractButton implements the painting of its icons (defaultIcon, disabledIcon, pressedIcon, etc.).
I have found the fields/getters/setters for said icons in AbstractButton, but apparently the assorted paint methods are inherited directly from JComponent. This begs the question how the icons are ever painted!? Obviously they are, but I could not find the code that does it.
Paiting of components in Swing is made by the Look-n-Feel. So if you want to find how the icon of a button is painted simply look the method paintIcon of the class BasicButtonUI. But each Look-n-Feel you use can provide different painting.
The icon in JButton is painted by the UI Class. And the UI class is defined by the Look and Feel. It is an implementation of javax.swing.plaf.ButtonUI.
JComponent.paint method get the ui property from the instance and invoke its update method (and is where the icon is painted). The UI of the instance is acquired by the UIManager.
You can se the paint method on Open JDK BasicButonUI
I hope I understand your question: are you trying to understand how does an icon get painted on an JButton? Take a look at the paint method from JComponent (its super super class). I'll add comments to make it easier to explain:
public void paint(Graphics g) {
boolean shouldClearPaintFlags = false;
//Check the size of the component: don't draw anything with a negative width or height!
if ((getWidth() <= 0) || (getHeight() <= 0)) {
return;
}
//Create new Graphics objects (why? to create the images)
Graphics componentGraphics = getComponentGraphics(g);
Graphics co = componentGraphics.create();
try {
RepaintManager repaintManager = RepaintManager.currentManager(this);
//Initialize a RepaintManager (JavaDoc says: "This class manages repaint requests, allowing the number of repaints to be minimized")
//Create a rectangle at the size of a graphics object
Rectangle clipRect = co.getClipBounds();
int clipX;
int clipY;
int clipW;
int clipH;
//If the rectangle is null, then give it default values
if (clipRect == null) {
clipX = clipY = 0;
clipW = getWidth();
clipH = getHeight();
} else { //otherwise, use its coordinates
clipX = clipRect.x;
clipY = clipRect.y;
clipW = clipRect.width;
clipH = clipRect.height;
}
//Ajust the clip widths and heights
if(clipW > getWidth()) {
clipW = getWidth();
}
if(clipH > getHeight()) {
clipH = getHeight();
}
//If your Component is placed on a JComponent (or extended class), then make adjustments
if(getParent() != null && !(getParent() instanceof JComponent)) {
adjustPaintFlags();
shouldClearPaintFlags = true;
}
//Check if the component is printing (private flag IS_PRINTING)
int bw,bh;
boolean printing = getFlag(IS_PRINTING);
//If the component is not printing its contents, and if the repain manager is double buffered, AND if not ancestor (subclass) is buffering AND if the components is buffered....
//JavaDoc says for RepaintManager.isDoubleBufferingEnabled(): "Returns true if this RepaintManager is double buffered. The default value for this property may vary from platform to platform."
if(!printing && repaintManager.isDoubleBufferingEnabled() &&
!getFlag(ANCESTOR_USING_BUFFER) && isDoubleBuffered()) {
//... then start painting the Graphics
repaintManager.beginPaint();
try {
repaintManager.paint(this, this, co, clipX, clipY, clipW, clipH);
} finally {
repaintManager.endPaint();
}
//if there is an exception, a try/finally is required to avoid the RepaintManager being "left in a state in which the screen is not updated" (JavaDoc)
}
else {
// Will ocassionaly happen in 1.2, especially when printing.
if (clipRect == null) {
co.setClip(clipX, clipY, clipW, clipH);
}
//Checks if the rectangle at the specified coordinates if obscured
if (!rectangleIsObscured(clipX,clipY,clipW,clipH)) {
//Then paint the graphics (or print if printing is true)
if (!printing) {
paintComponent(co);
paintBorder(co);
} else {
printComponent(co);
printBorder(co);
}
}
//Also paint the children (eg: a JPanel has a JLabel and a JButton as children if you add them to the panel) (or print if printing is true)
if (!printing) {
paintChildren(co);
} else {
printChildren(co);
}
}
} finally {
//Clean up!!
co.dispose();
if(shouldClearPaintFlags) {
setFlag(ANCESTOR_USING_BUFFER,false);
setFlag(IS_PAINTING_TILE,false);
setFlag(IS_PRINTING,false);
setFlag(IS_PRINTING_ALL,false);
}
}
}
In other words, there is a lot of code involved in painting Graphics objects, but the steps are pretty simple once you've analyzed the code:
Create new Graphics objects from the one passed in parameter;
Initialize a RepaintManager for repaint requests;
Check if the graphics can be painted, and do so if possible;
Finalize by repainting the children;
You're done!
If you want to know more on how the painting process is applied, then according to Oracle's Java documentation:
In Swing, painting begins with the paint method, which then invokes paintComponent, paintBorder, and paintChildren. The system will invoke this automatically when a component is first painted, is resized, or becomes exposed after being hidden by another window.
Programatic repaints are accomplished by invoking a component's repaint method; do not invoke its paintComponent directly. Invoking repaint causes the painting subsystem to take the necessary steps to ensure that your paintComponent method is invoked at an appropriate time.
You can invoke repaint multiple times from within the same event handler, but Swing will take that information and repaint the component in just one operation.
For components with a UI Delegate, you should pass the Graphics paramater with the line super.paintComponent(g) as the first line of code in your paintComponent override. If you do not, then your component will be responsible for manually painting its background. You can experiment with this by commenting out that line and recompiling to see that the background is no longer painted.
I took the code from here.
I hope I was able to help you,
Cheers

Categories

Resources