Weird bug involving background images in java - java

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

Related

How to paint a JPanel only by a JButton press?

This is a fragment of an application with several tabs hung off a JTabbedPanel:-
I have generated the image with code in the standard following manner:-
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(device.getVisualisation(), 0, 0, null);
}
So every time something happens to the application gui, the paintComponent method is called and the image displays. That's normal.
This paint event also happens if the tab focus changes, or the mouse passes over the tab as you'd expect. The problem is this takes several seconds as generating the graphic takes a lot of time. This delay is unavoidable and I accept it. You also get many paint events as the gui system does what it has to. Normally this would be okay, but with the processing delay, the gui stops /flashes /updates several times over the period of say 10 seconds.
I thought that I could deal with this by only manually calling repaint() from a "REFRESH" JButton somewhere on the gui. But I can't turn off the automatic repainting if you tough the tabs. How do I only paint a component by pressing a button, and not automatically?
I would do things a bit differently by first figuring out what is the expensive process here. It's not painting but rather the calculation and creation of the special image that is displayed by the painting. So with this in mind, rather than turning off painting, which would involve only kludges, instead store the image drawn an image field, and carefully control when it is recreated via the expensive device.getVisualisation() method.
If this method is truly long-running, then it don't call it within paintComponent, a method which never should hold cpu-intense or time crunching code, and in fact, the method should be called off of the Swing event thread, and instead within a background thread. Then when the background thread is done processing, update that same BufferedImage and call repaint(), and display the new image.
For example:
private Image image = null; // holds our image
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (image != null) {
// this will hardly take any time at all to run
g2d.drawImage(image, 0, 0, this);
}
}
public void myDrawImage() {
// create a SwingWorker for background threading work
new SwingWorker<Image, Void>() {
#Override
protected Image doInBackground() throws Exception {
// run this long-running code within this background thread
return device.getVisualisation();
};
#Override
protected void done() {
try {
// when the thread is done, get the new image,
// put it into our image field, and repaint the component
image = get();
repaint();
} catch (InterruptedException | ExecutionException e) {
// TODO handle any exceptions that occur with drawing
}
};
}.execute();
}
Now the image drawn will only change when and if your program specifically calls the myDrawImage() method, so that now the calling of the long-running code is under your total control.

Writing a vertical scrolling game in java

I am trying to write a game where the player is a character, falling interminably through the Earth. However, I am getting stuck at the very beginning, programming the scrolling background. My current approach is to create a JFrame and add to it an object of a class that extends JFrame. In this second class, I open the background image in the constructor. Then, in the first class, I create a new thread for the second class, and alternate sleeping and moving the y-coordinate of the background. Back in the second class, this movement triggers a repaint, and the image is drawn twice, once at the y-coordinate, and once at the y-coordinate minus the height of the JPanel. This current code gives the desired affect at any given frame, but the movement is slow and uneven. I think it has something to do with the amount of repaint requests, but I am fairly new to graphics in java. My question is, can this be fixed so the image move steadily across the screen, or should I try a completely different approach? If my current method is fundamentally flawed and can not be fixed, could you provide some incite as to how I can produce a smooth moving image? Thank you in advance.
This is my current code:
//ScrollingImage.java
import javax.swing.JFrame;
public class ScrollingImage {
public static void main(String[] args){
JFrame holder = new JFrame("New Game");
Background background = new Background();
Thread thread = new Thread(background);
holder.setSize(400, 400);
holder.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
holder.add(background);
holder.setVisible(true);
thread.start();
while (true){
background.move();
try {
thread.sleep(100);
}
catch (InterruptedException e){}
}
}
}
and:
//Background.java
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
public class Background extends JPanel implements Runnable {
private BufferedImage image;
private int topEdgeY;
public Background(){
try {
image = ImageIO.read(new File("background.png"));
}
catch (Exception e){}
topEdgeY = 0;
}
public void run(){
repaint();
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(image, 0, topEdgeY, getWidth(), getHeight(), this);
g.drawImage(image, 0, topEdgeY - getWidth(), getWidth(), getHeight(), this);
}
public void move(){
topEdgeY += 5;
if (topEdgeY == getWidth())
topEdgeY = 0;
repaint();
}
}
You need a proper game loop. Currently, your game sleeps 100ms after every iteration, that is (even if your updating and rendering takes no time) 10fps and will go even lower as your game doing more job.
Game loops is a huge topic and there are several good approaches for different situations like is it a multiplayer game or how much physics involved in the game but for your case a simple one will just be fine.
A good paper about game loops, hopefully this will makes you understand:
http://gameprogrammingpatterns.com/game-loop.html
And a pretty good example written in Java:
http://www.java-gaming.org/index.php?topic=24220.0

Draw on Jframe image

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);

Using JEditorPane from non-EventDispatchThread

I'm using a JEditorPane as a "rubber stamp" to render HTML text to a PDF. I need the text to wrap at specific widths, and need to apply a white "highlight" behind the text. As such, I'm creating a JEditorPane in the PDF rendering thread, setting the text and stylesheet, and then painting it to the PDF graphics. However, I'm getting an intermittent NullPointerException deep in the bowels of the HTML Editor Kit. This is reproducible with this SSCCE:
import javax.swing.*;
import javax.swing.text.View;
import javax.swing.text.html.HTMLDocument;
import java.awt.*;
import java.awt.image.BufferedImage;
/**
* #author sbarnum
*/
public class TextMarkerUtilsTest {
public static void main(String[] args) throws Exception {
Rectangle bounds = new Rectangle(255, 255);
BufferedImage image = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_RGB);
Graphics2D d = image.createGraphics();
d.setClip(bounds);
for (int i=0; i<1000; i++) {
JEditorPane renderHelper = new JEditorPane("text/html", "<html><body>This is my text.</body></html>");
HTMLDocument document = (HTMLDocument) renderHelper.getDocument();
document.getStyleSheet().addRule("foo{color:black;}");
View rootView = renderHelper.getUI().getRootView(renderHelper);
rootView.paint(d, bounds);
}
}
}
Running the above throws the following exception, usually after just a few times through the loop:
java.lang.NullPointerException
at sun.font.FontDesignMetrics$MetricsKey.init(FontDesignMetrics.java:199)
at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:267)
at sun.swing.SwingUtilities2.getFontMetrics(SwingUtilities2.java:949)
at javax.swing.JComponent.getFontMetrics(JComponent.java:1599)
at javax.swing.text.LabelView.getFontMetrics(LabelView.java:154)
at javax.swing.text.html.InlineView.calculateLongestWordSpanUseWhitespace(InlineView.java:246)
at javax.swing.text.html.InlineView.calculateLongestWordSpan(InlineView.java:191)
at javax.swing.text.html.InlineView.getLongestWordSpan(InlineView.java:177)
at javax.swing.text.html.ParagraphView.calculateMinorAxisRequirements(ParagraphView.java:140)
at javax.swing.text.BoxView.checkRequests(BoxView.java:918)
at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:551)
at javax.swing.text.html.ParagraphView.getMinimumSpan(ParagraphView.java:261)
at javax.swing.text.BoxView.calculateMinorAxisRequirements(BoxView.java:886)
at javax.swing.text.html.BlockView.calculateMinorAxisRequirements(BlockView.java:129)
at javax.swing.text.BoxView.checkRequests(BoxView.java:918)
at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:551)
at javax.swing.text.html.BlockView.getMinimumSpan(BlockView.java:361)
at javax.swing.text.BoxView.calculateMinorAxisRequirements(BoxView.java:886)
at javax.swing.text.html.BlockView.calculateMinorAxisRequirements(BlockView.java:129)
at javax.swing.text.BoxView.checkRequests(BoxView.java:918)
at javax.swing.text.BoxView.setSpanOnAxis(BoxView.java:326)
at javax.swing.text.BoxView.layout(BoxView.java:691)
at javax.swing.text.BoxView.setSize(BoxView.java:380)
at javax.swing.plaf.basic.BasicTextUI$RootView.setSize(BasicTextUI.java:1703)
at javax.swing.plaf.basic.BasicTextUI$RootView.paint(BasicTextUI.java:1422)
at com.prosc.msi.model.editor.TextMarkerUtilsTest$1.run(TextMarkerUtilsTest.java:40)
at java.lang.Thread.run(Thread.java:680)
Some interesting findings:
If the above runs in the Event Dispatch Thread, it works
If I take out the call to addRule("foo{color:black;}"), it works (I need to specify rules, but it doesn't seem to matter what the rule is, it fails if any rules are added)
The problem is in javax.swing.text.GlyphPainter1.sync(), where javax.swing.text.GlyphView.getFont() is returning null. By setting a conditional breakpoint, I see that the GlyphView in this case is a javax.swing.text.html.InlineView. Calling getFont() after the breakpoint has stopped returns a non-null font, so something is not being initialized in time.
I realize that the swing components are not thread-safe, but shouldn't I be able to instantiate a JEditorPane in a background thread and manipulate it safely in that background thread, as long as only the one thread is making calls to the component?
As you are using only lightweight components, headless mode may be an option. You can keep the work out of your GUI's EDT using ProcessBuilder, illustrated here.
public static void main(String[] args) throws Exception {
System.setProperty("java.awt.headless", "true");
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Rectangle bounds = new Rectangle(255, 255);
BufferedImage image = new BufferedImage(
bounds.width, bounds.height, BufferedImage.TYPE_INT_RGB);
Graphics2D d = image.createGraphics();
d.setClip(bounds);
for (int i = 0; i < 1000; i++) {
JEditorPane renderHelper = new JEditorPane(
"text/html", "<html><body>This is my text.</body></html>");
HTMLDocument document = (HTMLDocument) renderHelper.getDocument();
document.getStyleSheet().addRule("foo{color:black;}");
View rootView = renderHelper.getUI().getRootView(renderHelper);
rootView.paint(d, bounds);
}
}
});
}
Thanks to Marko for the suggestion to look for callbacks to the Event Dispatch Thread, I ended up finding one in HTMLDocument.styleChanged(). My subclass:
public class ThreadFriendlyHTMLDocument extends HTMLDocument {
#Override
protected void styleChanged(final Style style) {
// to fix GlyphPainter1.sync NullPointerException, we call this in the current thread, instead of the EDT
DefaultDocumentEvent dde = new DefaultDocumentEvent(0,
this.getLength(),
DocumentEvent.EventType.CHANGE);
dde.end();
fireChangedUpdate(dde);
}
}

Java Applet Buffering images

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.

Categories

Resources