I've been trying for a while to load an image into this JFrame for it to display it without success. Here is the code:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JOptionPane;
public class Main extends JPanel{
Bird bird = new Bird(this);
public void paint(Graphics g){
super.paint(g);
Graphics2D g2D = (Graphics2D) g;
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
bird.paint(g2D);
}
public static void main(String[] args)throws InterruptedException{
JFrame frame = new JFrame("Java Birds");
Main game = new Main();
frame.add(game);
frame.setSize(500, 300);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
while(true){
game.repaint();
Thread.sleep(10);
}
}
}
This is my Bird Class:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;
public class Bird {
private static final Image sprite = Toolkit.getDefaultToolkit().getImage("bird.jpeg");
private static final int DIAMETER = 30;
double g = 0.12, vy = 0, xo = 100, yo = 10;
private Main game;
public Bird(Main game){
this.game = game;
}
public void paint(Graphics2D g){
g.setColor(Color.BLACK);
g.drawImage(sprite, 30, 30, game);
}
}
When I run this nothing shows up onscreen, but if I place a g.fillOval instruction I do get a circle in the panel. Help much appreciated, please.
There are a cascade of issues, first...
public void paint(Graphics g){
super.paint(g);
Graphics2D g2D = (Graphics2D) g;
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
bird.paint(g2D);
}
You're overriding paint, it is highly unrecommended to do so, instead, it is recommended that you override paintComponent instead.
Another problem is...
Toolkit.getDefaultToolkit().getImage("bird.jpeg");
The problem with this is getImage(String) expects that the value you pass it refers to a file on the file system. In most cases, this is not true and the image is stored as an embedded resource, in which use you would need to use something more like...
Toolkit.getDefaultToolkit().getImage(Bird.class.getResource("bird.jpeg"));
or
Toolkit.getDefaultToolkit().getImage(Bird.class.getResource("/bird.jpeg"));
There is still no guarantee that the image is loaded and none of these approaches actually tells you when it has failed.
A better solution would be to use ImageIO to read the image, apart from supporting more formats, it will throw an IOException when it fails...
public class Bird {
private Image sprite;
//...
public Bird(Main game) throws IOException {
image = ImageIO.read(getClass().getResource("/bird.jpeg"));
This...
while(true){
game.repaint();
Thread.sleep(10);
}
Is also very dangerous, you've started this in the main method, but you've take no consideration into what thread main might be called in. While in "normal" operations, main is called by the JVM from what is known as the "main thread", there is no guarantee that this is how your main method is called. It might called by another class from the context of the EDT which would cause the program to freeze.
Generally you should either use a javax.swing.Timer or a separate thread all together.
To display an image which fills the entire panel, you should have the following. You can use ImageIO.read(File) to read in an image from a file (you can adjust the position and size of the image inside the paintComponent method). You may want to also see Graphics.drawImage`.
import javax.swing.*;
import java.awt.image.*;
import java.awt.*;
public class PictureFrame extends JComponent{
private final Image img;
public PictureFrame(final String file) throws IOException {
this(new File(file));
}
public PictureFrame(final File file) throws IOException {
this(ImageIO.read(file));
}
public PictureFrame(BufferedImage img){
this.img = img;
this.setPreferredSize(new Dimension(img.getWidth(), img.getHeight()));
}
public void paintComponent(Graphics g){
super.paintComponent(g);
g.drawImage(img, 0, 0, getWidth(), getHeight(), null);
}
}
Tester main:
public static final String TEST_FILE = "file path here";
public static void main(String... args) throws IOException {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
}
final JFrame frame = new JFrame();
final JComponent picture = new PictureFrame(TEST_FILE);
frame.setContentPane(picture);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 300);
frame.setVisible(true);
}
Related
I have been wanting to program a 2D game from scratch in Java for a while. The pixel aesthetic is one of my favorites, so I am aiming for a pixel 2D game. However, whenever I try to use BufferedImage to draw my tiles, the tiles become extremely distorted.
The tile drawn is actually bigger than the real tile and it seems like it has been stretched. Basically, say I have a 16x16 tile and I draw it. I can visually tell it is distorted when I run the program, and when I take a screenshot, I can measure the pixels and it has somehow become a 20x20.
I have also noticed that when I set a JFrame or a JPanel in the JFrame to a certain size, it is not the actual size that is produced. In my program I create a 320x320 JPanel and put it in a JFrame, but when I take a screenshot and measure the window, it comes up to about 399x399.
Can someone please tell me how to fix this. I stop every game project because the graphics keep looking like rubbish.
This is the Main class:
package main;
import javax.swing.SwingUtilities;
public class Main {
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Engine e = new Engine();
e.start();
}
});
}
}
This is the Engine class:
package main;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
public class Engine {
public JFrame f;
public void initFrame() {
f = new JFrame();
f.setTitle("Something");
f.setResizable(false);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
}
public void start() {
initFrame();
BufferedImage tree;
try {
tree = ImageIO.read(new File("res/boy_down_1.png"));
Panel p = new Panel(tree);
f.add(p);
f.pack();
} catch (IOException e) {
e.printStackTrace();
}
f.setVisible(true);
}
}
This is the Panel class:
package main;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class Panel extends JPanel {
BufferedImage i;
public Panel(BufferedImage image) {
i = image;
this.setDoubleBuffered(true);
this.setPreferredSize(new Dimension(320, 320));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(i, 20, 20, null);
g2d.dispose();
}
}
This is the 16x16 I am trying to draw
This is what my computer shows me
I have tried multiple ways to specify the size of the image, but Java seems to distort my image no matter what I do. Thank you in advance.
my JFrame does not show the image of my JLabel.
The JFrame is shown but without the background image.
Expected result was: JFrame that shows a background image ("stelle.png").
I'd greatly appreciate if anyone could help :-)
Thanks!
Simon
public static void main(String[] args) {
new Gui();
}
public class Label extends JLabel {
#Override protected void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g.drawImage(Var.quadro, 0, 0, 800,600, null);
repaint();
}
}
public class Var {
static BufferedImage quadro;
public Var(){
try {
quadro = ImageIO.read(new File("quadri/stelle.png"));
}
catch (IOException e) {
e.printStackTrace();
System.out.println("No picture");
}
}
}
public class Gui {
public Gui(){
JFrame rahmen = new JFrame();
rahmen.setSize(800,600);
rahmen.setLocationRelativeTo(null);
rahmen.setVisible(true);
rahmen.setResizable(false);
rahmen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
rahmen.setLayout(null);
rahmen.setTitle("Gioco");
Label label = new Label();
label.setVisible(true);
label.setBounds(0, 0, 800, 600);
rahmen.add(label);
}
}
Hi Have tweaked your solution below:
keep the package structure intact for the Test.java(can copy all code in it) and your pic stelle.png, refer the attached image below for this example to work seem less.
for incorporating changes in your own structure please keep image in a relative package quadri (refer the attached image, see how i kept it)
Please pay attention to my comments.
package com.demo.test.stack;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
//Main class for executing test
public class Test {
public static void main(String[] args) {
new Gui();
}
}
//this is you extended Label class be careful while importing
class Label extends JLabel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g.drawImage(Var.quadro, 0, 0, 800, 600, null);
repaint();
}
}
class Var {
static BufferedImage quadro;
//initializing the static variable in static class block (since you are using it directly)
static {
try {
//gettting the absolute path of your image
URL url = Test.class.getResource("quadri/stelle.png");
System.out.println(url.getPath());
quadro = ImageIO.read(new File(url.getPath()));
System.out.println("quadro: " + quadro);
} catch (IOException e) {
e.printStackTrace();
System.out.println("No picture");
}
}
}
//your feature class
class Gui {
public Gui() {
JFrame rahmen = new JFrame();
rahmen.setSize(800, 600);
rahmen.setLocationRelativeTo(null);
rahmen.setVisible(true);
rahmen.setResizable(false);
rahmen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
rahmen.setLayout(null);
rahmen.setTitle("Gioco");
Label label = new Label(); //<<this label is your extended label (i.e com.demo.test.stack.Label)and not awt label
label.setVisible(true);
label.setBounds(0, 0, 800, 600);
rahmen.add(label);
}
}
Let me know if you ave any other queries.
Regards and welcome to SO.
Cheers
I'm trying to make a simple GUI program without using JComponents.
Currently, I have a BufferedImage that I draw to off screen so that it doesn't flicker (or so I thought).
I made a new program here to replicate the issue:
package Main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Main {
private final static JFrame frame = new JFrame();
private final static Panel panel = new Panel();
public static void main(String[] args) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(new Dimension(1000, 750));
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
while (true) {
panel.setBackgroundColour(Color.WHITE);
panel.setBackgroundColour(Color.BLACK);
panel.repaint();
}
}
private static class Panel extends JPanel {
private final BufferedImage offScreen = new BufferedImage(1000, 750, BufferedImage.TYPE_INT_ARGB);
private final Graphics graphics = offScreen.getGraphics();
#Override
protected void paintComponent(Graphics graphics) {
graphics.drawImage(offScreen, 0, 0, null);
}
public void setBackgroundColour(Color colour) {
graphics.setColor(colour);
graphics.fillRect(0, 0, 1000, 750);
}
}
}
In the example above, I made the screen turn black, and then white (offscreen).
What I'd expect is that paintComponent() only displays the white screen.
Instead, a black screen is showed as well, but everything is flickered.
Am I just using Graphics2D incorrectly, or should I just use BufferStrategy to incorporate my double buffering needs?
My best guess is you have a race condition, where your while-loop is trying to update the BufferedImage, but Swing is also trying to paint it, meaning they are getting dirty updates between them. Also, you might be thrashing the Event Dispatching Thread, which could have it's own, long term issues.
After some playing around, I was able to get something like this to work...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
private final static JFrame frame = new JFrame();
private final static Panel panel = new Panel();
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setPreferredSize(new Dimension(1000, 750));
frame.setContentPane(panel);
frame.pack();
frame.setVisible(true);
}
});
while (true) {
panel.setBackgroundColour(Color.WHITE);
panel.setBackgroundColour(Color.BLACK);
panel.repaint();
try {
Thread.sleep(40);
} catch (InterruptedException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private static class Panel extends JPanel {
private BufferedImage offScreen = new BufferedImage(1000, 700, BufferedImage.TYPE_INT_ARGB);
#Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.drawImage(offScreen, 0, 0, this);
}
public void setBackgroundColour(Color colour) {
Graphics graphics = offScreen.getGraphics();
graphics.setColor(colour);
graphics.fillRect(0, 0, 1000, 700);
graphics.dispose();
}
}
public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
image.coerceData(true);
return image;
}
public static GraphicsConfiguration getGraphicsConfiguration() {
return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
}
}
All it does is injects a small delay (25fps) between the updates, allowing Swing time to render the result.
You have to remember at two things with Swing, repaint doesn't happen immediately and may not happen at all, depending on what the RepaintManager decides to do. Second, you don't control the painting process.
Swing uses a passive rendering algorithm, meaning that painting will occur when it's needed, many times without your knowledge or intervention. The best you can do is make suggestions to the framework when you want something updated
See Painting in AWT and Swing and Performing Custom Painting for more details.
Write a program that fills the window with a larrge ellipse. The ellipse shoud touch the window boundaries, even if the window is resized.
I have the following code:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
public class EllipseComponent extends JComponent {
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Ellipse2D.Double ellipse = new Ellipse2D.Double(0,0,150,200);
g2.draw(ellipse);
g2.setColor(Color.red);
g2.fill(ellipse);
}
}
And the main class:
import javax.swing.JFrame;
public class EllipseViewer {
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(150, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
EllipseComponent component = new EllipseComponent();
frame.add(component);
frame.setVisible(true);
}
}
in your EllipseComponent you do:
Ellipse2D.Double ellipse = new Ellipse2D.Double(0,0,getWidth(),getHeight());
I'd also recommend the changes given by Hovercraft Full Of Eels. In this simple case it might not be an issue but as the paintComponent method grows in complexity you realy want as little as possible to be computed in the paintComponent method.
Do not resize components within paintComponent. In fact, do not create objects or do any program logic within this method. The method needs to be lean, fast as possible, do drawing, and that's it. You must understand that you do not have complete control over when or even if this method is called, and you certainly don't want to add code to it unnecessarily that may slow it down.
You should create your ellipse in the class's constructor. To resize it according to the JComponent's size and on change of size, use a ComponentListener.:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JComponent;
public class EllipseComponent extends JComponent {
Ellipse2D ellipse = null;
public EllipseComponent {
ellipse = new Ellipse2D.Double(0,0,150,200);
addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
// set the size of your ellipse here
// based on the component's width and height
}
});
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.draw(ellipse);
g2.setColor(Color.red);
g2.fill(ellipse);
}
}
Caveat: code not run nor tested
According to the Javadoc, JComponent.repaint(long) is supposed to schedule a repaint() sometime in the future. When I try using it it always triggers an immediate repaint. What am I doing wrong?
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Repaint
{
public static final boolean works = false;
private static class CustomComponent extends JPanel
{
private float alpha = 0;
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2d.setPaint(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
alpha += 0.1;
if (alpha > 1)
alpha = 1;
System.out.println("alpha=" + alpha);
if (!works)
repaint(1000);
}
}
public static void main(String[] args)
{
final JFrame frame = new JFrame();
frame.getContentPane().add(new CustomComponent());
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
if (works)
{
new Timer(1000, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
frame.repaint();
}
}).start();
}
}
}
Note that the Javadoc says the method will cause a repaint to happen within (not after) the specified time.
If you want to schedule something to be repainted, then you should be using a Swing Timer. You should not be scheduling painting from withing the paintComponnt(..) method. You can't control when the paintComponent() method is called.
The parameter says tm - maximum time in milliseconds before update it does not say it won't do so immediately also the javadocs say
Repaints the component. If this
component is a lightweight component,
this results in a call to paint
within tm milliseconds.
If you search a little bit you find that this parameter is ignored in derived classes. ;)