I am using a transparent Stage in JavaFX which contains a Canvas.
The user can draw a rectangle in the canvas by dragging the mouse.
When he hits enter i am saving the image using:
try {
ImageIO.write(bufferedImage,extension,outputFile);
} catch (IOException e) {
e.printStackTrace();
}
To capture the image from screen i am using:
gc.clearRect(0, 0, getWidth(), getHeight()); // [gc] is the graphicsContext2D of the Canvas
// Start the Thread
new Thread(() -> {
try {
// Sleep some time
Thread.sleep(100);
// Capture the Image
BufferedImage image;
int[] rect = calculateRect();
try {
image = new Robot().createScreenCapture(new Rectangle(rect[0], rect[1], rect[2], rect[3]));
} catch (AWTException e) {
e.printStackTrace();
return;
}
} catch (Exception ex) {
ex.printStackTrace();
}
}).start();
The problem:
I don't know when the JavaFX Thread will clear the Canvas so i am using a Thread as you can see above which is waiting 100 milliseconds and then capture the screen.But it is not working all the times,what i mean is that sometimes the Canvas has not been cleared and i get this image(cause Canvas has not been cleared yet):
instead of this:
The question:
How i know when the JavaFX has cleared the Canvas so i can capture the screen after that?
Mixing Swing with JavaFX is not recommended although that's the only way i know to capture screen with Java...
I really hope there's a better way to achieve what you want than this, but the canvas will be cleared the next time a "pulse" is rendered. So one approach would be to start an AnimationTimer and wait until the second frame is rendered:
gc.clearRect(0, 0, getWidth(), getHeight());
AnimationTimer waitForFrameRender = new AnimationTimer() {
private int frameCount = 0 ;
#Override
public void handle(long timestamp) {
frameCount++ ;
if (frameCount >= 2) {
stop();
// now do screen capture...
}
}
};
waitForFrameRender.start();
Obviously, if you are doing the screen capture in a background thread, you need to also make sure you don't draw back onto the canvas before the capture occurs...
I'm new to programming. I'm trying to write a simple drawing program using java with processing. I need help with making it take a screenshot and save the image.
Here's what I have so far.
void setup(){
size(displayWidth, displayHeight);
background(255,255,255);
}
void keyPressed(KeyEvent SPACE){
background(255,255,255);
}
void draw(){
}
void mouseDragged()
{
strokeWeight(3);
stroke(0,0,0);
line(pmouseX, pmouseY, mouseX, mouseY);
}
I have no idea what you are trying to do with the code you provided with your question although here is how to make a screenshot and save it using Java.
try
{
Rectangle screenResolution = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
BufferedImage screenshot = new Robot().createScreenCapture(screenResolution);
ImageIO.write(screenshot, "PNG", new File("yourimageoutput.png"));
}
catch(IOException | AWTException error)
{
error.printStackTrace();
}
Here you can see the result:
If you want to change it to for example a 'jpeg' or 'bmp' image you have to change the second parameter of ImageIO.write() and the file extension.
I want to pick up the RBG value of the color in the image when the mouse clicks the position of the color. Actually, I put the image on the top left corner in the jFrame. I try to get the mouse location, for example, x= 190, y=80, which is near the last pixel of the image. However, the image size is 200x24. Therefore, I cannot convert the mouse pointer position to the pixel of the image. Is there any method to do this? Thank you.
Add more information:
I create a jframe and put a jlabel which is the image at the top left corner of the jframe. What I want to do it is: when I use the mouse point and click the position on the image, I would get the color of this position.
screen capture: http://i.stack.imgur.com/SjFhr.png
[when i use the mouse point to the black position of the image, it shows r=240,g=240,b=240]
frame.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
try {
System.out.println(getPointerColor());
Thread.sleep(1000);
} catch (AWTException awte) {
System.out.println("Error while getting pointer's color!");
} catch (InterruptedException ie) {
System.out.println("Error while sleeping!");
}
}
});
You can use the Robot class (see Documentation) to get the colour for a set of coordinates relative to the screen or the GraphicsDevice you want:
public Color getPixelColor(int x, int y) throws AWTException {
Robot robot = new Robot();
return robot.getPixelColor(x, y);
}
You can then retrieve the RGB values from that returned Color object. Make sure your coordinates are good though!
As an additional test, you can try running the following, which displays the pointer's pointed colour relative to the screen (in other words, the absolute cursor coordinates) every second:
import java.awt.AWTException;
import java.awt.Color;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
public class Test {
public static void main(String[] args) throws Exception {
while (true) {
try {
System.out.println(getPointerColor());
Thread.sleep(1000);
} catch (AWTException awte) {
System.out.println("Error while getting pointer's color!");
} catch (InterruptedException ie) {
System.out.println("Error while sleeping!");
}
}
}
public static Color getPointerColor() throws AWTException {
Point coordinates = MouseInfo.getPointerInfo().getLocation();
Robot robot = new Robot();
return robot.getPixelColor((int) coordinates.getX(), (int) coordinates.getX());
}
}
If it's a Swing UI, use JColorChooser for this: http://docs.oracle.com/javase/tutorial/uiswing/components/colorchooser.html. If you must do it manually, convert the mouse pointer position by subtracting a hard-coded offset.
I am making the game Asteroids. Everything functionally works properly, but originally I set the background color to black and had the objects represented by shapes that moved around on a Canvas. I've since changed the background to an Image and am working on changing the objects to be represented by images.
However, regardless of the background, I am having trouble repainting the image in a new location. You can see the path of each object after its been moved to each new location. I've been mostly focusing on the shot fired and I noticed if I fire shots all around the screen, the background is refreshed, but it almost seems to be completely at random. If anyone could help guide me in the right direction, that would be great! I've read several documents, textbooks, and watched several videos to try to understand.
package comets;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.sound.sampled.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
import java.net.URL;
// This class is primarily responsible for organizing the game of Comets
public class CometsMain extends JPanel implements KeyListener
{
// GUI Data
private JFrame frame; // The window itself
private JPanel playArea; // The area where the game takes place
private final int playWidth = 500; // The width of the play area (in pixels)
private final int playHeight = 500; // The height of the play area (in pixels)
// Game Data
private SpaceObject spaceObject;
private Ship ship; // The ship in play
private Shot s = new Shot(0, 0, 0, 0);
private LargeComet large = new LargeComet(0, 0, 0, 0);
private MediumComet medium = new MediumComet(0, 0, 0, 0);
private SmallComet small = new SmallComet(0, 0, 0, 0);
private Vector<Shot> shots; // The shots fired by the player
private Vector<Comet> comets; // The comets floating around
private boolean shipDead; // Whether or not the ship has been blown up
private long shipTimeOfDeath; // The time at which the ship blew up
// Keyboard data
// Indicates whether the player is currently holding the accelerate, turn
// left, or turn right buttons, respectively
private boolean accelerateHeld = false;
private boolean turnLeftHeld = false;
private boolean turnRightHeld = false;
private boolean slowDownHeld = false;
// Indicates whether the player struck the fire key
private boolean firing = false;
// Create Images
private Image background; // background image
private BufferedImage spaceShip = null;
private BufferedImage largeComet = null;
private BufferedImage mediumComet = null;
private BufferedImage smallComet = null;
private BufferedImage bullet = null;
private int type = AlphaComposite.SRC_OVER;
private float alpha = 0;
// Set up the game and play!
public CometsMain()
{
// Get everything set up
configureGUI();
configureGameData();
// Display the window so play can begin
frame.setVisible(true);
//Use double buffering
frame.createBufferStrategy(2);
//play music
playMusic();
// Start the gameplay
playGame();
}
private void playMusic(){
try {
URL url = this.getClass().getClassLoader().getResource("BackgroundMusic.wav");
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
Clip clip = AudioSystem.getClip();
clip.open(audioIn);
clip.start();
clip.loop(5);
} catch (Exception e) {
e.printStackTrace();
}
}
// Set up the initial positions of all space objects
private void configureGameData()
{
// Configure the play area size
SpaceObject.playfieldWidth = playWidth;
SpaceObject.playfieldHeight = playHeight;
// Create the ship
ship = new Ship(playWidth/2, playHeight/2, 0, 0);
// Create the shot vector (initially, there shouldn't be any shots on the screen)
shots = new Vector<Shot>();
// Read the comets from comets.cfg
comets = new Vector<Comet>();
try
{
Scanner fin = new Scanner(new File("comets.cfg"));
// Loop through each line of the file to read a comet
while(fin.hasNext())
{
String cometType = fin.next();
double xpos = fin.nextDouble();
double ypos = fin.nextDouble();
double xvel = fin.nextDouble();
double yvel = fin.nextDouble();
if(cometType.equals("Large"))
comets.add(new LargeComet(xpos, ypos, xvel, yvel));
else if(cometType.equals("Medium")){
comets.add(new MediumComet(xpos, ypos, xvel, yvel));
}
else
comets.add(new SmallComet(xpos, ypos, xvel, yvel));
}
}
// If the file could not be read correctly for whatever reason, abort
// the program
catch(FileNotFoundException e)
{
System.err.println("Unable to locate comets.cfg");
System.exit(0);
}
catch(Exception e)
{
System.err.println("comets.cfg is not in a proper format");
System.exit(0);
}
}
// Set up the game window
private void configureGUI()
{
// Load Images & Icons
// Background Image
try {
background = ImageIO.read(this.getClass().getClassLoader().getResource("galaxy.jpg"));
} catch (IOException e) {
}
// Space Ship Image
try {
spaceShip = ImageIO.read(this.getClass().getClassLoader().getResource("ship.png"));
} catch (IOException e) {
}
// Large Comet Image
try {
largeComet = ImageIO.read(this.getClass().getClassLoader().getResource("largecomet.png"));
} catch (IOException e) {
}
// Medium Comet Image
try {
mediumComet = ImageIO.read(this.getClass().getClassLoader().getResource("mediumcomet.png"));
} catch (IOException e) {
}
// Medium Comet Image
try {
smallComet = ImageIO.read(this.getClass().getClassLoader().getResource("smallcomet.png"));
} catch (IOException e) {
}
// bullet Image
try {
bullet = ImageIO.read(this.getClass().getClassLoader().getResource("bullet.png"));
} catch (IOException e) {
}
// Create the window object
frame = new JFrame("Comets");
frame.setSize(playWidth+20, playHeight+35);
frame.setResizable(false);
// The program should end when the window is closed
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(playWidth, playHeight);
// Set the window's layout manager
frame.setLayout(new FlowLayout());
// Create background
JLabel bgLabel = new JLabel( new ImageIcon(background.getScaledInstance(playWidth, playHeight, 0) ) );
bgLabel.setSize(playWidth, playHeight);
frame.setContentPane(bgLabel);
frame.pack();
// Create the play area
playArea = new JPanel();
playArea.setSize(playWidth, playHeight);
playArea.setFocusable(false);
playArea.setOpaque(false);
frame.add(playArea);
// Make the frame listen to keystrokes
frame.addKeyListener(this);
}
// The main game loop. This method coordinates everything that happens in
// the game
private void playGame()
{
while(true)
{
// Measure the current time in an effort to keep up a consistent
// frame rate
long time = System.currentTimeMillis();
// If the ship has been dead for more than 3 seconds, revive it
if(shipDead && shipTimeOfDeath + 3000 < time)
{
shipDead = false;
ship = new Ship(playWidth/2, playHeight/2, 0, 0);
}
// Process game events, move all the objects floating around,
// and update the display
if(!shipDead)
handleKeyEntries();
handleCollisions();
moveSpaceObjects();
// Sleep until it's time to draw the next frame
// (i.e. 32 ms after this frame started processing)
try
{
long delay = Math.max(0, 32-(System.currentTimeMillis()-time));
Thread.sleep(delay);
}
catch(InterruptedException e)
{
}
}
}
// Deal with objects hitting each other
private void handleCollisions()
{
// Anything that is destroyed should be erased, so get ready
// to erase stuff
Graphics g = playArea.getGraphics();
Graphics2D g2d = (Graphics2D) g;
g2d.setComposite(AlphaComposite.getInstance(type, alpha));
// Deal with shots blowing up comets
for(int i = 0; i < shots.size(); i++)
{
Shot s = shots.elementAt(i);
for(int j = 0; j < comets.size(); j++)
{
Comet c = comets.elementAt(j);
// If a shot has hit a comet, destroy both the shot and comet
if(s.overlapping(c))
{
// Erase the bullet
shots.remove(i);
i--;
repaint((int)shots.elementAt(i).getXPosition(), (int)shots.elementAt(i).getYPosition(), (int)(2*shots.elementAt(i).getRadius()), (int)(2*shots.elementAt(i).getRadius()));
// If the comet was actually destroyed, replace the comet
// with the new comets it spawned (if any)
Vector<Comet> newComets = c.explode();
if(newComets != null)
{
paintComponent(g);
comets.remove(j);
j--;
comets.addAll(newComets);
}
break;
}
}
}
// Deal with comets blowing up the ship
if(!shipDead)
{
for(Comet c : comets)
{
// If the ship hit a comet, kill the ship and mark down the time
if(c.overlapping(ship))
{
shipTimeOfDeath = System.currentTimeMillis();
shipDead = true;
spaceObject=ship;
paintComponent(g);
}
}
}
}
// Check which keys have been pressed and respond accordingly
private void handleKeyEntries()
{
// Ship movement keys
if(accelerateHeld)
ship.accelerate();
if(slowDownHeld)
ship.slowDown();
// Shooting the cannon
if(firing)
{
firing = false;
shots.add(ship.fire());
}
}
// Deal with moving all the objects that are floating around
private void moveSpaceObjects()
{
Graphics g = playArea.getGraphics();
// Handle the movements of all objects in the field
if(!shipDead)
updateShip(g);
updateShots(g);
updateComets(g);
}
// Move all comets and draw them to the screen
private void updateComets(Graphics g)
{
for(Comet c : comets)
{
spaceObject=c;
paintComponent(g);
// Move the comet to its new position
c.move();
paintComponent(g);
}
}
// Move all shots and draw them to the screen
private void updateShots(Graphics g)
{
for(int i = 0; i < shots.size(); i++)
{
Shot s = shots.elementAt(i);
// Erase the shot at its old position
paintComponent(g);
// Move the shot to its new position
s.move();
// Remove the shot if it's too old
if(s.getAge() > 180)
{
shots.remove(i);
i--;
}
// Otherwise, draw it at its new position
else
{
moveImage(g, s, (int)s.getXPosition(), (int)s.getYPosition());
paintComponent(g);
}
}
}
// Moves the ship and draws it at its new position
private void updateShip(Graphics g)
{
// Erase the ship at its old position
paintComponent(g);
// Ship rotation must be handled between erasing the ship at its old position
// and drawing it at its new position so that artifacts aren't left on the screen
if(turnLeftHeld)
ship.rotateLeft();
if(turnRightHeld)
ship.rotateRight();
ship.move();
// Draw the ship at its new position
moveImage(g, ship, (int)ship.getXPosition(), (int)ship.getYPosition());
paintComponent(g);
}
// Draws this ship s to the specified graphics context
private void drawShip(Graphics g, Ship s)
{
Graphics2D ship = (Graphics2D) spaceShip.getGraphics();
double x = Math.sin(s.getAngle());
double y = Math.cos(s.getAngle());
AffineTransform transformsave = AffineTransform.getRotateInstance(x, y, spaceShip.getWidth()/2, spaceShip.getHeight()/2);
AffineTransformOp transform = new AffineTransformOp( transformsave, AffineTransformOp.TYPE_BILINEAR );
// Figure out where the ship should be drawn
int xCenter = (int)s.getXPosition();
int yCenter = (int)s.getYPosition();
// Draw the ship body
g.drawImage(transform.filter(spaceShip, null), xCenter-10, yCenter-20, null);
ship.setTransform(transformsave);
}
public void setSpaceObject(SpaceObject s){
spaceObject=s;
}
public SpaceObject getSpaceObject(){
return spaceObject;
}
#Override
protected void paintComponent( Graphics g ){
super.paintComponent(g);
spaceObject=getSpaceObject();
int radius = (int)s.getRadius();
int xCenter = (int)s.getXPosition();
int yCenter = (int)s.getYPosition();
// Draw the object
if(spaceObject==s)
g.drawImage( bullet, xCenter-radius, yCenter-radius, this );
else if(spaceObject==large)
g.drawImage( largeComet, xCenter-radius, yCenter-radius, this );
else if(spaceObject==medium)
g.drawImage( mediumComet, xCenter-radius, yCenter-radius, this );
else if(spaceObject==small)
g.drawImage( smallComet, xCenter-radius, yCenter-radius, this );
else if(spaceObject==ship)
drawShip(g, ship);
}
public void moveImage(Graphics g, SpaceObject s, int x, int y){
int radius = (int)s.getRadius();
int xCenter=0, yCenter=0;
if(xCenter!=x || yCenter!=y){
xCenter= (int)s.getXPosition();
yCenter = (int)s.getYPosition();
repaint(xCenter, yCenter, radius*2, radius*2);
}
}
// Deals with keyboard keys being pressed
public void keyPressed(KeyEvent key)
{
// Mark down which important keys have been pressed
if(key.getKeyCode() == KeyEvent.VK_UP)
this.accelerateHeld = true;
if(key.getKeyCode() == KeyEvent.VK_LEFT)
this.turnLeftHeld = true;
if(key.getKeyCode() == KeyEvent.VK_RIGHT)
this.turnRightHeld = true;
if(key.getKeyCode() == KeyEvent.VK_SPACE)
this.firing = true;
//ADD DOWN TO SLOW DOWN SHIP!!!
if(key.getKeyCode() == KeyEvent.VK_DOWN)
this.slowDownHeld = true;
}
// Deals with keyboard keys being released
public void keyReleased(KeyEvent key)
{
// Mark down which important keys are no longer being pressed
if(key.getKeyCode() == KeyEvent.VK_UP)
this.accelerateHeld = false;
if(key.getKeyCode() == KeyEvent.VK_LEFT)
this.turnLeftHeld = false;
if(key.getKeyCode() == KeyEvent.VK_RIGHT)
this.turnRightHeld = false;
//ADD DOWN TO SLOW DOWN SHIP!!!
if(key.getKeyCode() == KeyEvent.VK_DOWN)
this.slowDownHeld = false;
}
// This method is not actually used, but is required by the KeyListener interface
public void keyTyped(KeyEvent arg0)
{
}
public static void main(String[] args)
{
// A GUI program begins by creating an instance of the GUI
// object. The program is event driven from that point on.
new CometsMain();
}
}
Don't use getGraphics. The problem with this, is it's simply a snap shot of what was last painted.
This is like taking a sheet of paper and repeatedly drawing on top of it, it makes a real mess real quick.
getGraphics can also return null.
In Swing painting is controlled by the RepaintManager which makes decisions about what and when things should be painted.
The basic concept you should be trying to follow is to...
Update the state of the game model
Update the view of the game model
Render the view to the screen
This can be achieved in a few different ways, but to start with, if you want to make updates to the UI, you should override the paintComponent method of a component that extends from something like JComponent.
When called, call super.paintComponent which will automatically prepare the Graphics context for painting.
To update the view...
You Could...
In a background thread, update the game model and request that the view be repainted
In the view's paintComponent method, re-draw the model
This is a relatively simple approach, but can, if not controlled well, can get the view and the model out of sync. You also need to ensure that the model is not changed while the view is been updated...
Or, You Could...
Create two (or more) BufferedImages
In a background thread, update the game model
In the background thread, update the "offscreen" buffer to represent the current game model state
Switch the buffered images (moving the "offscreen" buffer to the "active screen" and the "active screen" to the "offscreen")
Request that the view be updated
In the view's paintComponent method, simply draw the "active screen"
This is a more complex process, which will require you to ensure that the buffer's are the same size as the view, but means that the model can be updated independently of the view repaints. There is still the danger that you could change the "off" and "active" screen buffers while the view is been painted.
You could elevate this process slightly by using some kind of queue, where you place BufferedImages that can be used for rendering to (by popping them off the queue) and having the "view" push them back again once it has rendered it...
Or some combination of these, where you lock the switching of the "active" and "off" screen buffers to ensure that that "active" buffer isn't being painted.
Take a look at Performing Custom Painting and Painting in AWT and Swing for more details
For example...
Java Bouncing Ball
the images are not loading
Swing animation running extremely slow
Modern graphics applications use following approach:
For every frame repeat these steps
erase screen
draw background
draw your objects in proper order
do anything else
With this approach you don't need to track previous locations of your objects that can be very tricky because object may overlap.
Of course this will cause flickering, due to lack of performance. Various algorithms to prevent this are exists, take a look on this Documentation and this Question
you can just use getContentPane().repaint() every time you use repaint().
I'm developing a Tower Defense game using libGDX. I've just started and I am able to display the path, the "environment" and the enemies walking along the path.
I displayed the environment using a SpriteBatch-Object, like this:
public LevelController(Level level) {
spriteBach = new SpriteBach();
atlas = new TextureAtlas(Gdx.files.internal("images/textures/textures.pack"));
}
public void setSize() {
spriteBatch.setProjectionMatrix(this.cam.combined);
}
public void render() {
spriteBatch.begin();
drawTowerBases();
spriteBatch.end();
}
private void drawTowerBases() {
// for each tower base (=environment square)
TextureRegion towerBaseTexture = atlas.findRegion("TowerBase");
if(towerBaseTexture != null) {
spriteBatch.draw(towerBaseTexture, x, y, 1f, 1f);
}
}
This is working properly and the textures are displayed well: Tower Defense using spriteBatch
Now, I was wondering if it is possible to cache the background. Because it stays the same, there is no need to calculate it every time. I've found the SpriteCache through Google-Search. So I changed the code as follows:
public LevelController(Level Level) {
spriteCache= new SpriteCache();
atlas = new TextureAtlas(Gdx.files.internal("images/textures/textures.pack"));
spriteCache.beginCache();
this.drawTowerBases();
this.spriteCacheEnvironmentId = spriteCache.endCache();
}
public void setSize() {
this.cam.update();
spriteCache.setProjectionMatrix(this.cam.combined);
}
public void render() {
spriteCache.begin();
spriteCache.draw(this.spriteCacheEnvironmentId);
spriteCache.end();
}
private void drawTowerBases() {
// for each tower base (=environment square)
TextureRegion towerBaseTexture = atlas.findRegion("TowerBase");
if(towerBaseTexture != null) {
spriteCache.add(towerBaseTexture, x, y, 1f, 1f);
}
}
And now the game looks like this: Tower Defense using spriteCache
For me it seems as if the transparency is not rendered properly. If I take images without transparency, everything works fine. Does anyone have an idea, why this happens and how I can fix this?
Thank you in advance.
Taken from the SpriteCache documentation:
Note that SpriteCache does not manage blending. You will need to enable blending (Gdx.gl.glEnable(GL10.GL_BLEND);) and set the blend func as needed before or between calls to draw(int).
I guess there is not much more to say.