OK so here's my code: http://www.so.pastebin.com/Qca4ERmy
I am trying to use buffers so the applet won't flicker upon redraw() but it seems I am having trouble. The applet still flickers....
Help?
Thank you.
I made a quick video about this problem: http://www.vimeo.com/12035196
Create a Swing applet. Swing is double buffered by default so you should not have this problem. Start with the section from the Swing tutorial on How to Make Applets for the proper way to create a Swing applet.
The best way I've done it is to create another image the same size as your applet, draw to that, then in your paint / update method copy the contents of that image to your graphics object. You have to make sure that you aren't updating the other image when you draw to your applet otherwise it will cause flicker. Drawing should probably be done in another Thread as well, just to make things a little easier to understand.
I don't have access to my code so the following might be a little off (and the code may not be the most efficient):
public class MyApplet extends Applet {
Image offscreen;
boolean pageFlipped = false;
Thread drawingThread;
public void init() {
offscreen = createImage(this.getWidth(), this.getHeight());
drawingThread = new Thread(new DrawingLoop());
drawingThread.start();
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
if (!pageFlipped) {
g.drawImage(offscreen, 0, 0);
pageFlipped = true;
}
}
class DrawingLoop implements Runnable {
public void run() {
while (true) {
Graphics g = offscreen.getGraphics();
if (pageFlipped) {
// do your graphics code here
pageFlipped = false;
}
}
}
}
}
Hope this helps!
-Dan
You can try to solve this issue using a BufferedImage, in this way you just create a BufferedImage that is compatible with your frame and then draw everything there before blitting the whole image onto the JFrame's content.
A better approach is to use automatic buffering with BufferStrategy class, you can read a tutorial about it here.
Related
This is my first time trying to use another thread in java, could someone tell me how to make it work please? I've read others topics about it, but I didn't find a solution.
I'd like to draw a gif in another thread (drawn at at a random position and during the duration of its animation).
The problem is that drawImage() in the second thread just doesn't do anything. My counter works well (it prints 1.. 2.. 3 ...), but no image is drawn (or I can't see it).
The condition is false at the begining, then it is true at one moment (to create only one new thread and no more), then it is false again.
if (condition) {
(new ThreadGif(this,g)).start();
}
However when I remove the condition in paintComponent(), it draws something which means that drawImage() works. So when it creates lots of new threads, every image of the gif is drawn at a random location and it starts the gif again and again (and the counter(s) still work well).
This could be fine, but I don't think creating thousands of new thread is the answer : I just need one. And also, I need just one random position for each gif, not one different for each image of the gif.
I hope I've been clear enough. Please help me understand how to make it work :) Thank you very much.
Here are simplified versions of my two classes :
ThreadGif.java :
public class ThreadGif extends Thread {
Screen screen;
Graphics g;
boolean running = true;
public ThreadGif(Screen screen, Graphics g) {
this.g = g;
this.screen = screen;
}
public void run() {
int aleaX = new Random().nextInt(300)/100;
int aleaY = new Random().nextInt(300)/100;
int compt = 1;
while (running) {
g.drawImage(new ImageIcon("res/feu.gif").getImage(), screen.tailleCase*aleaX, screen.tailleCase*aleaY, screen.tailleCase*2, screen.tailleCase*2, screen);
System.out.println("thread " + compt);
compt++;
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
e.printStackTrace ();
}
}
}
}
Screen.java :
public class Screen extends JPanel implements Runnable {
Thread thread = new Thread(this);
public Screen(Frame frame) {
this.frame = frame;
thread.start();
}
public void paintComponent(Graphics g) {
g.clearRect(0, 0, this.frame.getWidth(), this.frame.getHeight());
if (condition) {
(new ThreadGif(this,g)).start();
}
}
public void run() {
while (running) {
repaint();
try {
Thread.sleep(sleepTime);
} catch(InterruptedException e) {
e.printStackTrace ();
}
}
System.exit(0);
}
}
Pretty simple: you mixed two approaches up.
The paintComponent-method launches a new ThreadGif every time it's called and ThreadGif itself paints within it's thread until it's terminated, but without refreshing the screen.
These two approaches combined might either result in strange behaviour, e.g. two images painted where pieces overlay each other, or the new ThreadGif simply renders the new image every-time you repaint the screen.
Solution: Start by assigning each class specific tasks, without mixing anything up, or splitting tasks between two classes. E.g.:
ThreadGif doesn't paint anything itself, but repaints the Screen. The Screen can request the image that should be displayed from ThreadGif.
Make ThreadGif an own Component that handles it's own rendering and ommit the Screen-class from painting anything.
My problem is that when I run my program I get a white screen and text from an earlier build instead of the background image that's suppose to be displayed. I've deleted all the code that was associated with that build.
I've looked around for help and all the threads I've seen say to write the code how I've set it up. I don't understand where the displayed background is even coming from.
Here is the relivent code:
package tactics;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.swing.JFrame;
public class Tactics2 extends JFrame{
private Screen s;
private BufferedImage bg;
private BufferedImage template;
private boolean loaded = false;
public static void main(String[] args) throws IOException{
DisplayMode dm = new DisplayMode(1024, 768, 16, DisplayMode.REFRESH_RATE_UNKNOWN);
Tactics2 t = new Tactics2();
t.run(dm);
}
//run method
public void run(DisplayMode dm) throws IOException{
loadpics();
s = new Screen();
try{
s.setFullScreen(dm, this);
try{
Thread.sleep(5000);
}catch(InterruptedException ex){}
}finally{
s.restoreScreen();
}
}
public void loadpics() throws IOException{
bg = new BufferedImage(1024, 768, BufferedImage.TYPE_INT_RGB);
template = new BufferedImage(1024, 768, BufferedImage.TYPE_INT_RGB);
ChaosBack cb = new ChaosBack();
bg = cb.ChaosBack(bg, template);
loaded = true;
repaint();
}
#Override
public void paint(Graphics g){
if(loaded){
g.drawImage(bg, 0, 0, null);
}
}
}
You've broken the paint chain
#Override
public void paint(Graphics g){
if(loaded){
g.drawImage(bg, 0, 0, null);
}
}
Basically, you've failed to call super.paint. Graphics is a shared resource, that is, everything painted for a given paint cycle uses the same Graphics context.
Part of the job of the paint chain is to prepare it for painting by clearing the Graphics context.
You should avoid overriding paint of a top level container for a number reasons. It's not double buffered, so it may flicker as it's updated and it doesn't take into consideration the frame decorations, meaning you can end up painting underneath the borders of the frame, instead within the viewable area.
You'd better of creating a custom component, extending from something like JPanel and overriding it's paintComponent method (making sure you call super.paintComponent)
Thread.sleep(5000); is a REALLY bad idea within a Swing application. It's possible to actually stop your application cold and stop it from been updated/painted or respond to any user interaction.
Swing is not thread safe. This means that all changes to the UI must be made from within the context of the Event Dispatching Thread.
Take a look at:
Performing Custom Painting
Painting in AWT and Swing
Concurrency in Swing
Initial Threads
How to Use Swing Timers
For details and ideas
I want to read a file to get some points and then draw these points on an image. Currently, I am able to draw values on the image, but the file is read three times and the rectangles are drawn three times. I don't know where is the problem. Below is the code. The Read() function works fine seperately so I didn't include it in the code.
P.S: I am beginner in JAVA and don't know much about JFrame and Jcomponent.
public class LoadImageApp extends JComponent {
BufferedImage img;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, null);
Read(g);// This is the function in which I read a file.
}
public LoadImageApp() {
try {
img = ImageIO.read(this.getClass().getResource("/New York.jpg"));
} catch (IOException e) {
}
}
public Dimension getPreferredSize() {
if (img == null) {
return new Dimension(100,100);
} else {
return new Dimension(img.getWidth(null), img.getHeight(null));
}
}
public static void main(String[] args) {
JFrame f = new JFrame("Load Image Sample");
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
LoadImageApp img = new LoadImageApp();
f.add(img);
f.pack();
f.setVisible(true);
}
}
Do not, do not, DO NOT read from a file from within any painting method such as paintComponent(...). :)
That method should be for painting only. The more you slow it down, the less responsive your GUI will seem.
You cannot control how many times the method gets called, since it is not under direct control by you, the programmer.
You can't even control fully if the paintComponent method gets called, since the JVM might decide that too many requests for redraw are being stacked up, and it may not honor all of them.
Instead
read in the data once in a constructor or something similar.
I would create a read method that stores my points in an ArrayList<Point>, and then inside of the paintComponent method, iterate through that ArrayList using a for loop and draw them.
If the points don't change during your program's run, you could even draw them directly on to the BufferedImage by getting its Graphics context and using that to paint the points on to the image, and then show the new BufferedImage in your paintComponent method.
Other suggestions:
That empty catch block where you read your image is a dangerous thing to do. It's the coding equivalent of driving a motorcycle with your eyes closed. At least print out a stacktrace.
The WindowListener is not needed. Instead simply set your JFrame's defaultCloseOperation to JFrame.EXIT_ON_CLOSE: f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
I would like to write a program in java...
I want to set the shape of the window(a JFrame) to a set of PNG Images(with a transparent background).
(Actually, I would like to make the window change its shape continual, and it look like an animation!)
And, I read images from files,save them to a array, then, I use class GeneralPath to get the area of my animated character(in png images), save it to areaArray.
After all things are done, I start the paint thread. It works well...But sometimes the window would flash(ah...I mean a flicker happened, but the background color I saw when flashing is transparent, I could saw my desktop wallpaper!).
I don't want to see the flicker/flash again, would someone help me? Thanks!
P.S. : Sorry for my poor English!
public class JCustomFrame extends JFrame implements Runnable
{
private final int max_frame=18; //Here is the max numbers of my png images
private BufferedImage[] BufImageArray; //The array to save all the png images
private BufferedImage nowImage; //Save the image to be drawn in this turn
private int counter; //Indicate which png image to be drawn
private Area[] areaArray; //Save the shapes of the animated character in each png image
public void run()// a thread function to paint the frame continual
{
while(true){
if(counter==max_frame)counter=0;
nowImage=BufImageArray[counter];
setShape(areaArray[counter]);
repaint();
try{
Thread.sleep(100);
}catch(InterruptedException e){
System.out.println("Thread.sleep error!");
}
counter++;
}
}
public JCustomFrame()
{
super();
setUndecorated(true);
setBackground(new Color(0,0,0,0));
counter= 0;
//...some codes here
new Thread(this).start();
}
public void paint(Graphics graphic)
{
graphic.drawImage(nowImage,0,0,this);
}
}
Here is a sample code to run the program:
import javax.swing.*;
public class MainFrame
{
public static void main(String[] args)
{
JCustomFrame myFrame = new JCustomFrame();
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setSize(300,400);
myFrame.setVisible(true);
return ;
}
}
I modified 2 lines above, the "png file name" and the "max_frame" to apply the new image files.
I found that if I put the same program on Windows rather than my OpenSuse, it works very well(without the flicker), here I upload all the pack of my source(include the image file).
Here my code is.
Thanks again.
==================================================================================
Thanks Andrew Thompson for suggestions.
This time, I try to delete the codes unrelated to the problem, and paste a gif to show the situation. The codes above isn't runnable, but the source code in the link works well.
P.S. The flicker/flash happened in random frame, isn't completely the same as the gif shows.
('cause I could only add a transparent panel in my gif image at a fixed sequence)
Thanks!!
I have to write a simple Java app which can load pictures, show it in a GUI form, allow the user to apply some transformation, and show the transformed picture.
My solution is working fine, but the UI is flickering a bit, because the repaint method called too often (for example when the user scaling the image with a JSlider)
My code looks like this:
public class ImageCanvas extends Canvas
{
private BufferedImage image;
// ...
#Override
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
if(image != null)
{
// I draw out the image...
}
}
public void setImage(BufferedImage image)
{
this.image = image;
this.repaint();
}
public void setRotation(double rotation)
{
this.rotation = rotation;
this.repaint();
}
public void setScale(double scaleX, double scaleY)
{
//set the scaling field, then repaint ....
}
// and so on...
}
And, of course, I have an ImageCanvas control on my main UI, and I simply call the public methods (see for example the "setRotation" method above) which repaint the canvas area. I know it's a simple question, but I don't even find a DoubleBuffered property on the Canvas...
Any help appreciated.
Double buffering is built-in for Swing (i.e. JComponent derived) classes.
If you want built-in double-buffering, you should extend JPanel rather than Canvas, and override paintComponent, not paint.
If you can use JPanel than go for it. Please make sure you are not overriding the JPanel.paint method, override JPanel.paintComponent instead.
See this link for details.
Usually graphic lags in these applications can be caused by setting a empty variable at the top of the script, then changing its value, then waiting for the repaint to update it. You could try changing the:
setRotation(double rotation);
so that it rotates the image in that method.
Just a general thing I happen to see while dealing with graphics.