Libgdx V1.3.1 Splash Screen Tween Error: Exception in thread "LWJGL Application" java.lang.RuntimeException: No TweenAccessor was found for the target - gradle

I am having a difficult time implementing the Universal Tween Engine in my android, desktop, and html libgdx projects (Note that this is a problem with libgdx version 1.3.1 that uses gradle).
I have followed the instructions on the github wiki by using the fileTree method shown in the link below.
https://github.com/libgdx/libgdx/wiki/Universal-Tween-Engine
I managed to set the build path of the CORE and ANDROID projects to include the two tween engine jars. The classes get imported easily but when runnning the DESKTOP project I get the following error:
Exception in thread "LWJGL Application" java.lang.RuntimeException: No TweenAccessor was found for the target
at aurelienribon.tweenengine.Tween.build(Tween.java:787)
at aurelienribon.tweenengine.Tween.build(Tween.java:79)
at aurelienribon.tweenengine.BaseTween.start(BaseTween.java:85)
at aurelienribon.tweenengine.TweenManager.add(TweenManager.java:61)
at aurelienribon.tweenengine.BaseTween.start(BaseTween.java:98)
at com.infinitybit.killtheclouds.Screens.Splash.show(Splash.java:58)
at com.badlogic.gdx.Game.setScreen(Game.java:61)
at com.infinitybit.killtheclouds.KillTheClouds.create(KillTheClouds.java:14)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:136)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)
Here is my code
Splash.java
package com.infinitybit.killtheclouds.Screens;
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenManager;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.infinitybit.killtheclouds.KillTheClouds;
import com.infinitybit.killtheclouds.TweenStuff.SplashAccessor;
public class Splash implements Screen {
public static int SCREEN_WIDTH = Gdx.graphics.getWidth();
public static int SCREEN_HEIGHT = Gdx.graphics.getHeight();
private KillTheClouds game;
private SpriteBatch splashpaper;
private Sprite splashimage;
private TweenManager tweenManager;
public Splash(KillTheClouds game) {
this.game = game;
}
#Override
public void show() {
// TweenSTUFF
tweenManager = new TweenManager();
Tween.registerAccessor(Sprite.class, new SplashAccessor());
splashpaper = new SpriteBatch();
Texture splashTexture = new Texture("images/splash/logo.png");
splashimage = new Sprite(splashTexture);
// image size
splashimage.setSize(SCREEN_WIDTH / 1.5f, SCREEN_HEIGHT / 2);
// center image
splashimage.setPosition(SCREEN_WIDTH / 2 - splashimage.getWidth() / 2,
SCREEN_HEIGHT / 2 - splashimage.getHeight() / 2);
Tween.set(splashTexture, SplashAccessor.ALPHA).target(0)
.start(tweenManager);
Tween.to(splashTexture, SplashAccessor.ALPHA, 3).target(1)
.start(tweenManager);
Tween.to(splashTexture, SplashAccessor.ALPHA, 2).target(0).delay(2)
.start(tweenManager);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// update tween
tweenManager.update(delta);
splashpaper.begin();
splashimage.draw(splashpaper);
splashpaper.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
}
}
SplashAccessor.java
package com.infinitybit.killtheclouds.TweenStuff;
import aurelienribon.tweenengine.TweenAccessor;
import com.badlogic.gdx.graphics.g2d.Sprite;
public class SplashAccessor implements TweenAccessor<Sprite> {
public static final int ALPHA = 0;
#Override
public int getValues(Sprite target, int tweenType, float[] returnValues) {
switch (tweenType) {
case ALPHA:
returnValues[0] = target.getColor().a;
return 1;
default:
assert false;
return -1;
}
}
#Override
public void setValues(Sprite target, int tweenType, float[] newValues) {
float defRed = target.getColor().r;
float defGreen = target.getColor().g;
float defBlue = target.getColor().b;
switch(tweenType){
case ALPHA:
target.setColor(defRed, defGreen, defBlue, newValues[0]);
break;
default:
assert false;
}
}
}

Your splashTexture isn't the type of Sprite it is Texture!!!
That is your problem.
Create splashSprite from splashTexture, and call this:
Tween.to(splashSprite, SplashAccessor.ALPHA, 3).target(1)
.start(tweenManager);
Hope it helps!

Related

JavaFX resets my colors when adding buttons

I am using JavaFx to create a front-end to the cli application called spicetify. I am not using an fxml file for the layout instead I am using different classes for layout purposes.
One such class is the Sidebar class. In it I define how the sidebar should look and then create an object of it on the window/page that I need. Whenever I add buttons to the sidebar The colors of the window where the Sidebar object is created go blank/white.
I am unable to find anything by googling and hope that the information provided is enough.
Screenshot of the window without buttons
Screenshot of the window with buttons
Project Structure
Sidebar class:
package spicetify;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
public class Sidebar {
BaseWindow baseWindow = new BaseWindow();
private String[] buttonIconLocation = {
"file:src/main/resources/spicetify/images/buttons/home.png",
"file:src/main/resources/spicetify/images/buttons/theme.png",
"file:src/main/resources/spicetify/images/buttons/extension.png",
"file:src/main/resources/spicetify/images/buttons/edit.png",
};
private ImageView[] buttonIcon = new ImageView[4];
private VBox sidebar;
private Button[] buttons = new Button[4];
public Sidebar(int height, int width, String html, Parent root){
this.sidebar = new VBox();
this.sidebar.setPrefHeight(height);
this.sidebar.setPrefWidth(width);
this.sidebar.setStyle("-fx-background-color: " + html);
for (int i = 0; i < buttonIcon.length; i++){
buttonIcon[i] = new ImageView(buttonIconLocation[i]);
baseWindow.transform(buttonIcon[i], 50, 50, true);
}
for (int i = 0; i < buttons.length; i++){
buttons[i] = new Button("", buttonIcon[i]);
sidebar.getChildren().add(buttons[i]);
buttons[i].setTranslateX(20);
buttons[i].setTranslateY(i * 100);
buttons[i].setStyle("-fx-background-color: " + html);
}
}
public String[] getButtonIconLocation() {
return buttonIconLocation;
}
public void setButtonIconLocation(String[] buttonIconLocation) {
this.buttonIconLocation = buttonIconLocation;
}
public ImageView[] getButtonIcon() {
return buttonIcon;
}
public void setButtonIcon(ImageView[] buttonIcon) {
this.buttonIcon = buttonIcon;
}
public VBox getSidebar() {
return sidebar;
}
public void setSidebar(VBox sidebar) {
this.sidebar = sidebar;
}
}
BaseWindow class:
package spicetify;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
public class BaseWindow{
private Parent root;
private Scene scene;
private Stage stage;
private String title;
private int width;
private int height;
private int minWidth;
private int minHeight;
private String htmlColor;
public int getMinWidth() {
return minWidth;
}
public void setMinWidth(int minWidth) {
this.minWidth = minWidth;
}
public int getMinHeight() {
return minHeight;
}
public void setMinHeight(int minHeight) {
this.minHeight = minHeight;
}
public String getHtmlColor() {
return htmlColor;
}
public void setHtmlColor(String htmlColor) {
this.htmlColor = htmlColor;
}
public Parent getRoot() {
return root;
}
public void setRoot(Parent root) {
this.root = root;
}
public Scene getScene() {
return scene;
}
public void setScene(Scene scene) {
this.scene = scene;
}
public Stage getStage() {
return stage;
}
public void setStage(Stage stage) {
this.stage = stage;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public void transform(ImageView view, int width, int height, boolean preserveRatio){
view.setFitWidth(width);
view.setFitHeight(height);
view.setPreserveRatio(preserveRatio);
}
public void start(Stage stage){
stage.setScene(scene);
stage.setTitle(title);
stage.show();
}
}
HomeWindow class:
package spicetify;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class HomeWindow extends BaseWindow {
private ImageView logoView;
private ImageView preview;
private Parent root;
private Scene scene;
BaseWindow baseWindow = new BaseWindow();
Sidebar sidebar = new Sidebar(baseWindow.getHeight(), 100, "#282828", root);
public HomeWindow(int width, int height, String html){
this.logoView = new ImageView(new Image("file:src/main/resources/spicetify/images/essentials/logo.png"));
this.preview = new ImageView(new Image("file:src/main/resources/spicetify/images/essentials/Preview.png"));
this.root = new BorderPane();
baseWindow.setWidth(width);
baseWindow.setMinWidth(width);
baseWindow.setHeight(height);
baseWindow.setMinHeight(height);
baseWindow.setHtmlColor(html);
}
public void start(Stage stage){
((BorderPane) root).setLeft(sidebar.getSidebar());
baseWindow.transform(logoView, 600, 700, true);
((BorderPane) root).setCenter(logoView);
baseWindow.setStage(stage);
baseWindow.setTitle("Spicetify");
baseWindow.setRoot(root);
baseWindow.setScene(new Scene(baseWindow.getRoot(), baseWindow.getWidth(), baseWindow.getHeight(), Color.web(baseWindow.getHtmlColor())));
baseWindow.getStage().setScene(baseWindow.getScene());
baseWindow.getStage().setMinWidth(baseWindow.getMinWidth());
baseWindow.getStage().setMinHeight(baseWindow.getMinHeight());
baseWindow.getStage().setTitle(baseWindow.getTitle());
baseWindow.getStage().show();
}
}
Default Modena CSS has a gray background in panes.
When controls are loaded CSS will be applied to the entire scene, as controls use CSS.
Without any controls, CSS (for performance in straight rendering of graphics primitives) will only be applied to the scene if you specifically apply it.
For more information, and steps you can take to remove the default color from pane backgrounds, see the related question:
JavaFX 8 tooltip removes transparency of stage

Animating a sprite with JavaFX on key input

I'm new to JavaFX but have a good understanding of object orientated Java. The following program is a combination of two examples, one that animates and moves shapes , the other animates an object on a mouse button press. Much of the functionality has been removed or changed for my needs.
I've searched through many examples but haven't found one I fully understand regarding moving a sprite and animating on key press.In my program I'm sure that I'm not using the right classes to create the game object, even though with some tweaking I'm sure it could work.
I added some println functions to test the animation. The problem seems to be that the KeyFrame part in the walkSouth animation isn't working/playing.
My question is:
Should I be using different JavaFX classes to create the sprite-sheet animation?
Can this code be easily adapted to function so I can get a better understanding of how JavaFX works.
Here is the main class:
package testing;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private enum UserAction{
NONE,NORTH,SOUTH;
}
private static int APP_W = 200;
private static int APP_H = 200;
private Scene scene;
private UserAction action = UserAction.NONE;
private Timeline timeline = new Timeline();
private boolean running = true;
private int FPS = 60;
private Parent createContent(){
Pane root = new Pane();
root.setPrefSize(APP_W,APP_H);
Image cat_image = new Image("file:res/cata.png");
GameObject obj = new GameObject(cat_image,12,8);
obj.setTranslateX(100);
obj.setTranslateY(100);
KeyFrame frame = new KeyFrame(Duration.millis(1000/FPS), event -> {
if(!running)
return;
switch(action){
case NORTH:
obj.setTranslateY(obj.getTranslateY()-1);
break;
case SOUTH:
obj.walkSouth();
obj.setTranslateY(obj.getTranslateY()+1);
break;
case NONE:
obj.pauseAnimation();
break;
}
});
timeline.getKeyFrames().add(frame);
timeline.setCycleCount(Timeline.INDEFINITE);
root.getChildren().add(obj);
return root;
}
private void restartGame(){
stopGame();
startGame();
}
private void stopGame(){
running = false;
timeline.stop();
}
private void startGame(){
timeline.play();
running = true;
}
public void start(Stage primaryStage) throws Exception{
scene = new Scene(createContent());
scene.setOnKeyPressed(event -> {
switch (event.getCode()) {
case W:
action = UserAction.NORTH;
break;
case S:
action = UserAction.SOUTH;
break;
}
});
scene.setOnKeyReleased(event -> {
switch (event.getCode()) {
case W:
action = UserAction.NONE;
break;
case S:
action = UserAction.NONE;
break;
}
});
primaryStage.setTitle("Simple Animation");
primaryStage.setScene(scene);
primaryStage.show();
startGame();
}
public static void main(String[] args) {
launch(args);
}
}
Here is the GameObject class:
package testing;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.geometry.Rectangle2D;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.util.Duration;
/**
* Created by matt on 26/02/17.
*/
public class GameObject extends Pane {
ObjectImage objectImage;
public GameObject( Image image, int columns, int rows){
objectImage = new ObjectImage(image,columns,rows);
getChildren().setAll(objectImage);
}
public void pauseAnimation(){
getChildren().setAll(objectImage);
objectImage.pauseAnimation();
}
public void walkSouth(){
getChildren().setAll(objectImage);
objectImage.walkSouth();
}
}
class ObjectImage extends ImageView {
private Rectangle2D[] clips;
private double width,height;
private Timeline timeline = new Timeline();
public ObjectImage(Image image,int columns,int rows){
width = image.getWidth()/columns;
height = image.getHeight()/rows;
clips = new Rectangle2D[rows*columns];
int count=0;
for(int row =0;row < rows;row++ )
for(int column = 0 ; column < columns; column++,count++)
clips[count] = new Rectangle2D(width * column, height * row,width,height);
setImage(image);
setViewport(clips[0]);
}
public void pauseAnimation(){
timeline.pause();
}
public void walkSouth(){
System.out.println("walk south test");
IntegerProperty count = new SimpleIntegerProperty(0);
KeyFrame frame = new KeyFrame( Duration.millis(1000/5), event -> {
if(count.get() < 2) count.set(count.get()+1);
else count.set(0);
setViewport(clips[count.get()]);
System.out.println("frame test");
});
timeline.setCycleCount(timeline.INDEFINITE);
timeline.getKeyFrames();
timeline.play();
}
}
This is the sprite-sheet image I'm working with
This is the outcome
As hinted by the comment, you did forget to add the frame in the walkSouth method. (Also you set each picture frame in walkSouth method to 200ms. Did you meant to change that?) Here's the code after changing:
public void walkSouth(){
System.out.println("walk south test");
IntegerProperty count = new SimpleIntegerProperty(0);
KeyFrame frame = new KeyFrame( Duration.millis(1000/FPS), event -> {
if(count.get() < 2) count.set(count.get()+1);
else count.set(0);
setViewport(clips[count.get()]);
});
timeline.setCycleCount(timeline.INDEFINITE);
timeline.getKeyFrames().add(frame); //This was the offending line.
timeline.play();
}
To answer your first question, yes there are many other options of classes you could use. Two options you could do is use the AnimationTimer or the Transition class. Here's a brief explanation for both (with code samples).
AnimationTimer is called every cycle or frame of rendering, which I believe you might be wanting this one:
public void walkSouth(){
System.out.println("walk south test");
IntegerProperty count = new SimpleIntegerProperty(0);
AnimationTimer tmr = new AnimationTimer() {
#Override
public void handle(long nanoTime)
{
//nanoTime specifies the current time at the beginning of the frame in nano seconds.
if(count.get() < 2) count.set(count.get()+1);
else count.set(0);
setViewport(clips[count.get()]);
}
};
tmr.start();
//call tmr.stop() to stop/ pause timer.
}
If however, you don't want an animation to be called each frame, you could extend Transition. A transition has an frac (fractional) value ranging from 0 to 1 that increases with respect to time. I'm not going to go into a whole lot detail, but I'm sure you could look up some more information on the api.
public void walkSouth(){
System.out.println("walk south test");
IntegerProperty count = new SimpleIntegerProperty(0);
Transition trans = new Transition() {
{
setCycleDuration(Duration.millis(1000 / 60.0));
}
#Override
public void interpolate(double frac)
{
if (frac != 1)
return;
//End of one cycle.
if(count.get() < 2) count.set(count.get()+1);
else count.set(0);
setViewport(clips[count.get()]);
}
};
trans.setCycleCount(Animation.INDEFINITE);
trans.playFromStart();
//Use trans.pause to pause, trans.stop to stop.
}

Can't create animation in libgdx

I'm trying to create animation but keep getting errors
package com.leopikinc.bobdestroyer;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
public class MainMenu implements Screen{
Texture background_main;
TextureRegion[] background_textures;
Animation background_animation;
SpriteBatch batch;
TextureRegion current_frame;
float stateTime;
BobDestroyer game;
public MainMenu(BobDestroyer game){
this.game = game;
}
#Override
public void show() {
background_main = new Texture(Gdx.files.internal("main_menu_screen/Background.png"));
//TextureRegion[][] temp = new TextureRegion.split(background_main, 128, 72);
//background_textures = new TextureRegion[3];
for(int i = 0; i<3; i++){
background_textures[i] = new TextureRegion(background_main,0, 0+72*i, 128, 72+72*i);
}
background_animation = new Animation(0.2f,background_textures);
}
#Override
public void render(float delta) {
// Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
stateTime += Gdx.graphics.getDeltaTime();
current_frame = background_animation.getKeyFrame(stateTime, true);
batch.begin();
batch.draw(current_frame, 0, 0);
batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
}
this is my code and then i try to launch it i get
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.leopikinc.bobdestroyer.MainMenu.show(MainMenu.java:31)
at com.badlogic.gdx.Game.setScreen(Game.java:61)
at com.leopikinc.bobdestroyer.BobDestroyer.create(BobDestroyer.java:11)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:143)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:120)
How to fix this? And also when i use TextureRegion[][] temp = new TextureRegion.split(background_main, 128, 72);Eclipse says
TextureRegion.split cannot be resolved to a type
TextureRegion.split cannot be resolved to a type
You are getting an error because you are using keyword new
Do something like this and your error will be fixed
TextureRegion[][] tmp = TextureRegion.split(background_main, background_main.getWidth()/TOTAL_COLS, background_main.getHeight()/TOTAL_ROWS);
Your second error is because you have commented the line where you are initializing your array, you need to uncomment this
//background_textures = new TextureRegion[3];

Image Location?

I am having trouble simply importing an image using this code. Where should the Image be stored? I thought it had to be in a folder within the source folder and in this case called ImageIcon but I am not sure... thanks to anybody who helps!
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
class Surface extends JPanel {
private Image mshi;
public Surface() {
loadImage();
setSurfaceSize();
}
private void loadImage() {
mshi = new ImageIcon("mushrooms.jpg").getImage();
}
private void setSurfaceSize() {
Dimension d = new Dimension();
d.width = mshi.getWidth(null);
d.height = mshi.getHeight(null);
setPreferredSize(d);
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(mshi, 1, 1, null);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
public class DisplayImage extends JFrame {
public DisplayImage() {
initUI();
}
private void initUI() {
setTitle("Mushrooms");
add(new Surface());
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
DisplayImage ex = new DisplayImage();
ex.setVisible(true);
}
});
}
}
Do you try to put the image in the same directory as your class file Surface ?
Ok, sorry i had not seen the class DisplayImage.
The best way i think to do it, create a separate class file for Surface.
Write in Surface class file, a constructor with parameter. The parameter will be the image path. It will be simple for you to change the image if you want after.
Something like this :
public class Surface extends JPanel {
private Image mshi;
public Surface(String imagePath) {
mshi = new ImageIcon(imagePath).getImage();
setSurfaceSize();
}
private void setSurfaceSize() {
Dimension d = new Dimension();
d.width = mshi.getWidth(null);
d.height = mshi.getHeight(null);
setPreferredSize(d);
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(mshi, 1, 1, null);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
You don't need to write the method loadImage(), it will be probably used in your class because it is private and it just do one simple thing.
Concerning the structure of your project directory make something like this
Project directory\src
Project directory\src\Surface.class
Project directory\src\DisplayImage.class
Project directory\images
Project directory\images\mushrooms.jpg
Ps : Sorry for my english, it still under construction.

JavaFX : Canvas to Image in non GUI Thread

I have to visualize lot of data (real-time) and I am using JavaFX 2.2. So I have decided to "pre-visualize" data before they are inserted into GUI thread.
In my opinion the fastest way to do it (with antialliasing etc.) is let some NON GUI thread to generate image/bitmap and then put in GUI thread (so the UI is still responsive for user).
But I can't find way how to conver Canvas to Image and then use:
Image imageToDraw = convert_tmpCanvasToImage(tmpCanvas);
Platform.runLater(new Runnable() {
#Override
public void run() {
canvas.getGraphicsContext2D().drawImage(imageToDraw, data.offsetX, data.offsetY);
}
});
Thx for some usable answers. :-)
btw: I have made test app to show my problem.
package canvasandthreads02;
import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class CanvasAndThreads02 extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Paint");
final AnchorPane root = new AnchorPane();
final Canvas canvas = new Canvas(900, 800);
canvas.setLayoutX(50);
canvas.setLayoutY(50);
root.getChildren().add(canvas);
root.getChildren().add(btn);
Scene scene = new Scene(root, 900, 800);
primaryStage.setTitle("Painting in JavaFX");
primaryStage.setScene(scene);
primaryStage.show();
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Start painting");
/**
* Start Thread where some data will be visualized
*/
new Thread(new PainterThread(canvas, new DataToPaint())).start();
}
});
}
private class PainterThread implements Runnable{
private final DataToPaint data;
private final Canvas canvas;
public PainterThread(Canvas canvas, DataToPaint data){
this.canvas = canvas;
this.data = data;
}
#Override
public void run() {
long currentTimeMillis = System.currentTimeMillis();
Canvas tmpCanvas = new Canvas(data.width, data.height);
GraphicsContext graphicsContext2D = tmpCanvas.getGraphicsContext2D();
graphicsContext2D.setFill(data.color;);
for (int i = 0; i < data.height; i++) {
for (int j = 0; j < data.width; j++) {
graphicsContext2D.fillRect(j, i, 1, 1); //draw 1x1 rectangle
}
}
/**
* And now I need still in this Thread convert tmpCanvas to Image,
* or use some other method to put result to Main GIU Thread using Platform.runLater(...);
*/
final Image imageToDraw = convert_tmpCanvasToImage(tmpCanvas);
System.out.println("Canvas painting: " + (System.currentTimeMillis()-currentTimeMillis));
Platform.runLater(new Runnable() {
#Override
public void run() {
//Start painting\n Canvas painting: 430 \n Time to convert:62
//long currentTimeMillis1 = System.currentTimeMillis();
//Image imageToDraw = tmpCanvas.snapshot(null, null);
//System.out.println("Time to convert:" + (System.currentTimeMillis()-currentTimeMillis1));
canvas.getGraphicsContext2D().drawImage(imageToDraw, data.offsetX, data.offsetY);
}
});
}
}
private class DataToPaint{
double offsetX = 0;
double offsetY = 0;
Color color;
int width = 500;
int height = 250;
public DataToPaint(){
Random rand = new Random();
color = new Color(rand.nextDouble(), rand.nextDouble(), rand.nextDouble(), rand.nextDouble());
offsetX = rand.nextDouble() * 20;
offsetY = rand.nextDouble() * 20;
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
use Canvas' snapshot(...) method to create a WritableImage from the Canvas' content. ^^
Works fine for me.
I know this is a really old question, but just for anyone who cares:
There is now a second version of canvas.snapshot that takes a callback and works asynchronously!
public void snapshot(Callback<SnapshotResult,Void> callback,
SnapshotParameters params,
WritableImage image)

Resources