Java image display issue - java

Dear wonderful people of stackoverflow
A group of my friends are attempting to make a level editor in Java.
We have a Jpanel instead of a Jframe and we are trying to put small images onto the Jpanel from a filepath saved as a string. In the end we want a list of images that you can just drop on. So far we have tried a few methods with no luck.
We can load the images, however we can't get these images to actually display, What would be the best means of solving said problem?
below is a sample of what we have so far.
EnemyPlacementGrid = new JPanel();
EnemyPlacementGrid.addMouseListener(new MouseAdapter() {
//#Override
public int mouseX;
public int mouseY;
public void mouseClicked(MouseEvent arg0) { //what happens when you click in the EnemyPlacementGrid
System.out.println("Correct Area for placement");
mouseX = arg0.getX();
mouseY = arg0.getY();
//System.out.println("X:" + mouseX + ", Y:" + mouseY );
Enemy newEnemy = workingEnemy.cloneSelf();
newEnemy.setLocation(mouseX, mouseY);
System.out.println("newEnemy object: " + newEnemy);
System.out.println(newEnemy.weaponList);
currentWave.addEnemy(newEnemy);
System.out.print(currentLevel);
}
});
Any and all help is greatly appreciated.
UPDATE:
As of now I have an image appearing, however I can't update said image. Note code below:
public void run() {
try {
BufferedImage img = ImageIO.read(new File(IMG_PATH));
ImageIcon icon = new ImageIcon(img);
WaveScreen frame = new WaveScreen();
JPanel panel = (JPanel)frame.getContentPane();
JLabel label = new JLabel();
label.setIcon(new ImageIcon("images/map_on.png"));// your image here
panel.add(label);
frame.setVisible(true);
panel.add(label);
panel.repaint();
} catch (Exception e) {
e.printStackTrace();
}
update, method tried from comments:
Graphics2D g = null;
Graphics2D g2 = (Graphics2D)g;
Image imageVariable = new ImageIcon("images/map_on.png").getImage();
g.drawImage(imageVariable, mouseX, mouseY, null);

Well, i'd say to try using Graphics, meaning you need to override the paint method; i'd recommend that you put the mouseX and mouseY as global variables though…
// creating global image variable for use later
Image imageVariable = new ImageIcon("image path").getImage();
public void paintComponent(Graphics g) {
// here you could either create a Graphics2D object
// Graphics2D g2 = (Graphics2D)g;
// or you could use the g parameter as it is, doesn't matter.
// use the global variable for the image to be drawn onto the screen
// use the global value of the mouseX and mouseY for where you click the mouse
// to place the image, and this should be it
g.drawImage(imageVariable, mouseX, mouseY, null);
}
Hope this helps!

If the game is simple, user2277872's solution will work and you can use graphics2D from java. However, if you are planning on a more sophisticated game (lots of interaction, lots of textures), then the default Java framework for 2D graphics will prove to be too slow.
If you are planning on such a game, I can highly recommend either learning OpenGL or using an existing framework for graphics, such as
JMonkeyEngine (http://jmonkeyengine.com/)
or
Slick (http://slick.cokeandcode.com/index.php)
More information: What should I use to display game graphics?

Related

How to save a specific part of a JPanel?

I am working on a chess game and I would like to let the player choose the board's colors. Therefore I will use this method:
static void createBoard(Graphics g) {
Color bright = new Color(255, 225, 181); //player chooses color
Color dark = new Color(188, 141, 105); //player chooses color
boolean darkTile = false;
for (int y = spaceY; y < (spaceY + BOARDHEIGHT); y += TILESIZE) {
for (int x = spaceX; x < (spaceX + BOARDWIDTH); x += TILESIZE) {
if (darkTile) {
g.setColor(dark);
} else {
g.setColor(bright);
}
g.fillRect(x, y, TILESIZE, TILESIZE);
darkTile = !darkTile;
}
darkTile = !darkTile;
}
BufferedImage overlay;
try {
overlay = ImageIO.read(new File("overlay.png"));
JLabel label = new JLabel(new ImageIcon(overlay));
g.drawImage(overlay, spaceX, spaceY, BOARDWIDTH, BOARDHEIGHT, null);
} catch (IOException e) {}
}
This I would like to save as a BufferedImage, so I don't have to run this method all the time.
So how can I save just this part of my JPanel, without the stuff outside of the chess board? (there will be more painted)
This I would like to save as a BufferedImage,
Don't know that your need to save the BufferedImage to a file. You can just create a BufferedImage to be used by the application when the application starts. You can then recreate the BufferedImage if any of the user colors change.
You can paint directly to a BufferedImage:
BufferedImage image = new BufferedImage(boardSize, boardSize, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = image.createGraphics();
// draw the squares onto board
g2d.dispose();
Now your createBoard() method should probably return the BufferedImage so it can be used by your application.
You put in certain efforts to put up your question, so lets honor that with some thoughts to get you going.
First of all: you have an empty catch block {}. That is bad practice. This simply eats up any error messages you get. That is not helpful. Either allow that exception to bubble up and stop your application; or at least print its contents - so that you understand what happens.
And given your comment: you never now if there will be errors. Especially when doing IO, all sorts of things can go wrong. Please believe me: empty catch blocks are bad practice; and you should not train yourself to accept them.
Second thought: don't go for that yet. As convenient as it might sound; but saving a background picture doesn't add much value at this point.
You don't need to worry about this code; it is executed once when your application comes up.
So, the real answer here: focus on the features you want to implement; and don't get distracted with pre-mature optimizations.

Image flickering upon being repainted by the mouseDragged method

As the title suggests, my problem is that I want to be able to drag an image.
In this specific case, I want to drag an image from one JPanel (or rather my own subclass) into another (different) subclass of JPanel. Therefore, I added an MouseListener to my JPanel subclass, so that upon clicking a certain area in the panel, an image is chosen to be painted on the JFrame (subclass). Here's some code so you'll understand my problem:
public void mousePressed(MouseEvent e) {
int x = e.getX();
int y = e.getY();
if (x >= 10 && x < 42 && y >= 10 && y < 42) {
image = barracks; //barracks is a predefined image, created in the constructor
dragBuilding = true;
PixelMain.pixelMain.repaint(); //pixelMain is an instance of the JFrame subclass
}
}
//irrelevant code, e.g mouseMoved, ...
public void mouseDragged(MouseEvent e) {
if (dragBuilding) {
//System.out.println("GPanel mouseDragged");
PixelMain.pixelMain.repaint();
}
}
the JFrame subclass only contains the constructor and the following code:
public void paint(Graphics g) { //i would have used paintComponent, but it seems like JFrame does not have this method ...?
super.paint(g);
if (PixelMain.panelOffense.getDragBuilding()) { //panelOffense is an instance of the JPanel subclass, getDragBuilding returns a boolean that depends on whether the mouse is held down at the moment
Graphics2D g2 = (Graphics2D) g;
Rectangle2D tr = new Rectangle2D.Double((int)getMousePosition().getX(), (int)getMousePosition().getY(), 16, 16); //size of the texture
TexturePaint tp = new TexturePaint(PixelMain.panelOffense.getImg(), tr);
g2.setPaint(tp);
Rectangle2D r = (Rectangle2D) new Rectangle((int)getMousePosition().getX(), (int)getMousePosition().getY(), 16, 16); //area to fill with texture
g2.fill(r);
System.out.println("test");
}
}
Before you ask - I did move some code to other classes so it's called less often, but that's not the problem. Even if the paint method only draws a rectangle (directly on Graphics g, not Graphics2D), the rectangle flickers.
If anyone could help me figure out a solution, I'd be very thankful!
Note: I know it's probably not very elegant to draw on a JFrame or a subclass of JFrame, but I personally don't know an alternative.
Note 2: According to google/stackoverflow results or threads that I read, I should use a JPanel, which seems to be double-buffered (whatever that is, I didn't really understand that. but then again, it's almost 11 pm here). Hence, I could probably move all my components to a JPanel to solve the issue, but I wanted to try to solve the problem without doing that.
Note 3: Yes, the code belongs to a (strategy) game I'm writing, but considering that the problem is not really related to game development exclusively, I decided to post it here and not at game development stack exchange.

Writing Jpanel to Buffered Image stack overflow

I'm trying to write a JPanel Picture to a BufferedImage (later converted to Rendered Image). I am getting a stack overflow error in the AWT-EventQueue-0 thread for some reason and am not sure if there is a reason I have overlooked.
The code in question:
public BufferedImage createImage() {
int w = getWidth();
int h = getHeight();
BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
cp.paint(bi.getGraphics());
//debug script
File outputfile = new File("image"+index+".jpg");
try {
ImageIO.write(bi, "jpg", outputfile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
index++;
return bi;
}
The JPanel paintComponent
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
r = new Random(System.nanoTime());
int maxSize = 100;
int randX = r.nextInt(getWidth());
int randY = r.nextInt(getHeight());
int randWidth = r.nextInt(maxSize);
int randHeight = r.nextInt(maxSize);
Color color = new Color(r.nextInt(256), r.nextInt(256), r.nextInt(256));
Graphics2D g2d = (Graphics2D) g;
ovals.add(new MyCircles(randX, randY, randWidth, randHeight, color));
for (MyCircles c : ovals) {
c.paint(g2d);
}
g2d.setColor(getForeground());
repaint();
double current = ImageComparator.calcDistance((RenderedImage)createImage());
//debugging script
System.out.println("Current: " + current);
System.out.println("Previous" + previous);
if(current > previous) {
ovals.remove(ovals.size()-1);
}
else {
previous = current;
}
}
Any insight as to how to amend this issue would be greatly appreciated!
Remove the the call to repaint in paintComponent which causes that method to be called ad infinitum
Not directly related to your problem but, you should never use the Random class in the painting method. Every time you call the method the painting will change, so the image you create and save will not be the same as the image on the panel.
Also, you should not be adding ovals in the paint method for the same reason give above.
You need to create an addOval(...) method that will set the random color of the Oval and add the oval to the List. The painting code will just iterate through the List and paint the Oval.
You also should NOT be removing ovals in the painting code. Painting code is for painting only, not manipulating the objects painted.
You can also try the Screen Image class which is basically a more flexible version of your image creation code.
Of course you have infinite loop there:
here is how you call your methods:
createImage()
|__paint()
|__createImage() // again in ImageComparator.calcDistance line
|__paint()
|__createImage() // again in ImageComparator.calcDistance line
|__paint()
|__createImage() // again in ImageComparator.calcDistance line
|__paint()
TOY STORY 1 (Buzz) : to the infinite and beyond it :)
You never stop this cycle.
I suggest that you need to get the images and then compare them outsize of your paint. Let the paint just paint the image and do the comparison outside of it.

Java2D/Swing: Rendering a component with text anti aliasing to a BufferedImage

I would like to render a Java Swing component, e.g. a JButton, which I also put on a JFrame, to a BufferedImage. This works in general, but with a major drawback: Text anti aliasing, especially "LCD" anti aliasing mode, is not working when rendering to a BufferedImage.
I've put some example code together to demonstrate the problem, but first my system information:
OS: Windows 7 64 Bit
JVM: 1.6.0_26-b03 (32 Bit)
The following example code will create a simple JFrame, put a JButton on it and then renders the JButton to a file "test.png":
public class TextAntiAliasingTest
{
public TextAntiAliasingTest() throws IOException
{
// Create Test-Button which will be rendered to an image
JButton button = new JButton( "The Test-Button" );
button.setSize( 200, 70 );
button.setLocation( 200, 150 );
// Create JFrame
final JFrame frame = new JFrame();
frame.setSize( 800, 600 );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setLayout( null );
frame.setLocationRelativeTo( null );
frame.add( button );
// Show JFrame
SwingUtilities.invokeLater( new Runnable() {
#Override public void run() {
frame.setVisible( true );
}
});
// Render JButton to an BufferedImage
BufferedImage image = new BufferedImage( 800, 600, BufferedImage.TYPE_INT_ARGB );
Graphics2D g2d = (Graphics2D)image.getGraphics();
button.paint( g2d );
// Write BufferedImage to a PNG file
ImageIO.write( image, "PNG", new File( "test.png" ) );
}
public static void main( String[] args ) throws Exception
{
UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
System.setProperty( "awt.useSystemAAFontSettings", "lcd" );
new TextAntiAliasingTest();
}
}
The following image shows the difference between the JButton in the JFrame on screen, and the same rendered JButton in the image file:
Actually there is some text anti aliasing in the image, but not the LCD optimized anti aliasing which is shown on screen in the JFrame (this problem occurs also with the default LookAndFeel, not only with the "WindowsLookAndFeel").
I already tried to explicitely set the RenderingHint for text anti aliasing on the "g2d", the Graphics2D context of the BufferedImage like so:
g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
But it had no effect at all.
I urgently need to render the JButton to an image file like it is rendered on screen (this is just an example, actually I need to render some more complex components, which all suffer from that anti aliasing problem) and I hope there is a real solution without any nasty workaround like taking a screenshot or something.
I really appreciate any help - thanks a lot!
This is a JVM bug
I have encountered the same problem as you. And I have concluded that the problem is indeed with drawing to a translucent bitmap. I'm fairly certain that we're hitting: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6749069
Workaround
For now I draw into an opaque bitmap and blit it into my destination bitmap. If the background color behind your text is plain, this should work:
private void drawString(String text, int x, int y, Graphics2D g, Color bg){
// Prepare an off-screen image to draw the string to
Rectangle2D bounds = g.getFontMetrics().getStringBounds(text, g);
BufferedImage image = configuration.createCompatibleImage(
(int)(bounds.getWidth() + 1.0f),
(int)(bounds.getHeight() + 1.0f),
Transparency.OPAQUE);
Graphics2D ig = image.createGraphics();
// Fill the background color
ig.setColor(bg);
ig.fillRect(0, 0, image.getWidth(), image.getHeight());
// Draw the string
int x0 = 0;
int y0 = ig.getFontMetrics().getAscent();
ig.setColor(g.getColor());
ig.setRenderingHints(g.getRenderingHints());
ig.setFont(g.getFont());
ig.drawString(text, x0, y0);
ig.dispose();
// Blit the image to the destination
g.drawImage(image, x-x0, y-y0, null);
}
If your background is not a plain color, you'll have to render the text as white on black to a bitmap, A. Then fill another bitmap with your text color C. Then blit that to your destination image, D as: D += A*C or D = D*(1-A)+A*C or some other suitable blending function.
It's probably too late anyway. The problem could be related to a translucent BufferedImage image you're using. The following seems to work Ok:
BufferedImage image = new BufferedImage( 800, 600, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D)image.getGraphics();
g2d.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
While looking around I have found similar complains.
Either you want (a) your translucent BufferedImage to look as good as your JButton, or (b) you want your JButton and BufferedImage to look the same.
If (b), can you have a translucent JButton? Will it look the same as your translucent BufferedImage?
If (a) then a hard way to do it is, you could maybe paint the JButton to a non-translucent BufferedImage (if Java has a greyscale BufferedImage, use this). This will end up being the negative of your final BufferedImage's alpha channel (a black pixel becomes fully opaque, white fully transparent.) To get the colour for the pixels in the BufferedImage, you'll need to set any pixel that isn't transparent to black - the pixel's transparency should make it come out to be the grey you want.

Move multiple BufferedImage in Java2D?

How can I mousedrag different BufferedImages in Java2D?
For instance, if I have ten or more images, how can I move that images which my mouse is over?
Now I'm importing an BufferedImage with
BufferedImage img = new BufferdImage(new File("filename"));
And I'm painting this with Graphics2D with
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
g2d.drawImage(img, x1, y1, null);
g2d.drawImage(img2, x2, y2,null);
}
Everytime I'm moving on a image I'm repaint()-ing the entire screen.
My mousemove class is as follows
class MouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(MouseEvent e) {
x1 = e.getX() - (img.getWidth() / 2);
y1 = e.getY() - (img.getHeight() / 2);
repaint();
}
}
With this method I'm able to "drag" one picture, but what to do when I will drag more individually?
Use the BufferedImage to create an ImageIcon which you use to create a JLabel. Then you add the JLabel to the panel that uses a null layout. No custom painting code is required to do this.
Now if you want to drag the label around you can use the Component Mover.
You can try making a custom component that contains only a single image. Along with your painting and mouse motion handling code, the component overrides the contains method so that it returns true only if the coordinates are within the image.
These components are then stacked in a JLayeredPane, (hopefully) only moving the images that the mouse is on top of.
From what you ask I suppose that your current repainting logic is global. You need to apply it to every image you have. So, if you for instance display every image in JPanel attach MouseMotionListener to every such panel and make this logic happen in JPanel.
If you post more code - especially of the component you show your images in - I will be able to go into more details.
Here's is a simple example that implements dragging for either single- or multiple-selections. The object Node would correspond roughly to your object Card.
Addendum: Also considered the Overlap Layout mentioned in this answer to a related question. Instead of List<Node>, your program would manage a List<Card>, where each Card is a JLabel having a card image.
I should make tree arrays:
one for the x-values
one for the y-values
one for the BufferedImages
So, something like this:
int[] xValues = new int[10];
int[] yValues = new int[10];
BufferedImage[] imgs = new BufferedImage[10];
Then the
class MouseMotionHandler extends MouseMotionAdapter {
#Override
public void mouseDragged(MouseEvent e) {
for (int i = 0; i < 10; i++)
{
xValues[i] = e.getX() - (imgs[i].getWidth() / 2);
yValues[i] = e.getY() - (imgs[i].getHeight() / 2);
}
repaint();
}
}
Then paint them like this:
public void paintComponent(Graphics g) {
super.paintComponent(g);
g2d = (Graphics2D) g;
for (int i = 0; i < 10; i++)
{
g2d.drawImage(imgs[i], xValues[i], yValues[i], null);
}
}
I think something like this is what you need.
Here's the code for my JLayeredPane init. My problem here is that my images don't show up...
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new java.awt.Dimension(500, 410));
layeredPane.setBorder(javax.swing.BorderFactory.createTitledBorder(
"Center deck"));
for(BufferedImage imgs : images){
JLabel label = new JLabel(new ImageIcon(imgs));
layeredPane.add(label, JLayeredPane.DEFAULT_LAYER);
}
add(layeredPane);

Categories

Resources