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.
Related
I am currently working on an animation to compare two stock exchange algorithms. I am running the algorithms within the paint component extending JComponent. (not the best, but I don't care) I need to have the screen refresh half way through the paint component. I do not want it to have to get all the way through before it up dates the screen. The reason being is I have one algorithm with a nested while loop and the other without. How would I go about doing this?
public void paintComponent(Graphics g) {
//calls in the super class and calls the calibrate the graphics method
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
calibrateFrame( getHeight(), getWidth() );
//Clears the rectangle to avoid overlaying, makes it black
g2D.clearRect(0, 0, getWidth(), getHeight());
g2D.setColor(Color.BLACK);
g2D.fillRect(0, 0, getWidth(), getHeight());
//Draws the rectangles without the algorithm started
redraw(g2D, -1);
/**
*algorithms
*/
fastSpans[0] = 1;
slowSpans[0] = 1;
//Makes a new stack pushes 0 on the stack
Stack myStack = new Stack();
myStack.push(0);
//If it has not been sorted or paused, continue the algorithm
if (!(pause) && !(sorted)){
//The slower algorithm needs to start out at zero (j)
int j = indexValue-1;
g2D.setColor(Color.BLUE);
//Calculates the values for the X and Y coordinates for the
//new rectangle, along with the height
int slowY = calSlowY(j);
int slowX = calSlowX(j);
int curHeightSlow = (int) ((stocks[j]/maxStockValue)*maxHeight);
//Here is the actual algorithm
int k = 1;
boolean span_end = false;
//Nested While Loop
while (((j-k)>0) && !span_end){
if (stocks[j-k] <= stocks[j]){
k = k + 1;
// Draw the current component
// **********************
// DO REFRESH MID PAINT COMPONENT
}
else{ span_end = true; }
}
slowSpans[j] = k;
g2D.setColor(Color.WHITE);
for(int i = 0; i < numberOfStock ; i++){
}
if (!(indexValue >= numberOfStock)){
while (!( myStack.empty()) && (stocks[(int)myStack.peek()]) <= stocks[indexValue]){
myStack.pop();
}
if (myStack.empty()){
fastSpans[indexValue] = indexValue + 1;
}
else {
fastSpans[indexValue]= indexValue - (int) myStack.peek();
//System.out.println("Im in the else");
}
myStack.push(indexValue);
}
}
drawStrings(g2D);
}
I am running the algorithms within the paint component extending JComponent. (not the best, but I don't care)
But you should care, since this impacts on your problem and possible solution.
I need to have the screen refresh half way through the paint component. I do not want it to have to get all the way through before it up dates the screen. The reason being is I have one algorithm with a nested while loop and the other without. How would I go about doing this?
Then don't run the algorithm through paintComponent. Instead use a SwingWorker<Void, Image>, update a BufferedImage and pass that image to the GUI through the worker's publish/process method pair, calling repaint() at the same time. For more on how to use a SwingWorker, please have a look at: Lesson: Concurrency in Swing
Don't understand what half way means.
If it means half of the component, then the simple way is to using two JComponent.
If u means in same component one line updated but another line not updated.
What I understand the repaint is calling when it packs(), updateUI(), or caused by invalidate().
So in my view, the repaint() should only care about paint these lines/2D, and in another thread to execute these loops/generate these data. Once the data collection is finished just call updateUI()/paintImmediately.
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
repaint();
}
});
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.
For some reason my image loads after 1 second even though I call render before my thread.sleep ()
public void init (){
image=new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g= (Graphics2D) image.getGraphics();
running=true;
}
public void run (){
init();
while(running){
long start=System.nanoTime();
update();
render();
drawToScreen();
try{
thread.sleep(1000);
}
catch (Exception e){
e.printStackTrace();
}
}
}
public void update (){
}
public void render (){
g.clearRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.red);
for(int r=0; r<16; r++){
for(int c=0; c<16; c++){
MazeCell m=maze[15-r][c];
int y=15-m.getRow();
int x=m.getCol();
if(m.getWallUp()) g.drawLine(x*50, y*50, (x+1)*50, y*50);
if(m.getWallDown()) g.drawLine(x*50, (y+1)*50, (x+1)*50, (y+1)*50);
if(m.getWallLeft()) g.drawLine(x*50, y*50, x*50, (y+1)*50);
if(m.getWallRight()) g.drawLine((x+1)*50, y*50, (x+1)*50, (y+1)*50);
}
}
g.drawImage(mouseImage, mouse.getCol()*50, (15-mouse.getRow())*50, null);
}
public void drawToScreen (){
Graphics g2=getGraphics();
g2.drawImage (image, 0, 0, null);
g2.dispose();
}
Everything draws but at first there is a blank gray screen and it only loads after 1 second (the time the thread sleeps) even though render and drawToScreen are called first. I don't really know the reason.
Change your:
g2.dispose();
To:
g2.finalize();
Also, the description of the Graphics.drawImage() method states:
Draws as much of the specified image as is currently available. The image is drawn with its top-left corner at (x, y) in this graphics context's coordinate space. Transparent pixels in the image do not affect whatever pixels are already there.
This method returns immediately in all cases, even if the complete image has not yet been loaded, and it has not been dithered and converted for the current output device.
If the image has completely loaded and its pixels are no longer being changed, then drawImage returns true. Otherwise, drawImage returns false and as more of the image becomes available or it is time to draw another frame of animation, the process that loads the image notifies the specified image observer.
In other words, the call returns immediately, whether or not the image drawing has completed.
You can use MediaTracker to first make sure the image is loaded before you call Graphics.drawImage():
long timeout = 60000; // 60 seconds
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(image, 0);
try {
if (!tracker.waitForID( 0, timeout ) ) {
System.out.println( "Timed Out!" );
return;
}
}
catch( Exception ex) {
System.out.println( "Error waiting for the image to load." );
return;
}
boolean isFinished = g.drawImage(image, 0, 0, this);
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?
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);