31

Exporting JavaFX application as an animated GIF

 3 years ago
source link: http://fxapps.blogspot.com/2015/01/exporting-javafx-application-as.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
To try to export an animation from a JavaFX app to an animated GIF, I started on this thread in StackOverflow, then this class from Eliot Kroo, which I am sharing on gist in case his site goes down.

Right now I am going to try his class on a JavaFX application and I will share the results here.

A sample animation

This is the animation I generated using my code :
jfx2.gif
The source for this animation is here:

import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javafx.animation.FadeTransition; import javafx.animation.Interpolator; import javafx.animation.ParallelTransition; import javafx.animation.RotateTransition; import javafx.animation.ScaleTransition; import javafx.animation.SequentialTransition; import javafx.animation.Transition; import javafx.application.Application; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.scene.text.FontPosture; import javafx.scene.text.FontWeight; import javafx.stage.Stage; import javafx.util.Duration;

/** * * @author william */ public class GifExporterTest extends Application {

Transition master;

@Override public void start(Stage stage) {

Label lblTarget = new Label("JavaFX"); StackPane spContainer = new StackPane(lblTarget); Button btnStart = new Button("Start"); Button btnStop = new Button("Stop"); CheckBox chkExport = new CheckBox("Export to gif"); VBox root = new VBox(10, spContainer, new HBox(30, btnStart, btnStop, chkExport));

spContainer.setPrefSize(300, 250); lblTarget.setFont(Font.font("Tahoma", FontWeight.BOLD, FontPosture.REGULAR, 40));

Scene scene = new Scene(root, 600, 500); createAnimations(lblTarget);

stage.setTitle("Hello World!"); stage.setScene(scene); stage.show();

btnStart.setOnAction(e -> { try { master.playFromStart(); if(chkExport.isSelected()) GitExporterFX.captureNow(spContainer, (int) master.getTotalDuration().toMillis(), "/home/wsiqueir/jfx2.gif", 100, true); } catch (IOException ex) { Logger.getLogger(GifExporterTest.class.getName()).log(Level.SEVERE, null, ex); } }); btnStop.setOnAction(e -> master.stop()); btnStart.disableProperty().bind(master.statusProperty().isEqualTo(Transition.Status.RUNNING)); }

void createAnimations(Node target) { Duration firstDuration = Duration.millis(2000); ScaleTransition st = new ScaleTransition(firstDuration, target); st.setFromX(0.1); st.setToX(1); st.setFromY(0.1); st.setToY(1); st.setInterpolator(Interpolator.LINEAR);

RotateTransition rt = new RotateTransition(firstDuration.divide(2), target); rt.setByAngle(360); rt.setInterpolator(Interpolator.LINEAR); rt.setCycleCount(2);

FadeTransition ft = new FadeTransition(Duration.millis(300), target); ft.setFromValue(1); ft.setToValue(0.1); ft.setCycleCount(2); ft.setAutoReverse(true);

ParallelTransition pt = new ParallelTransition(st, rt); master = new SequentialTransition(pt, ft); }

Based on Eliot's class, I created one which will take an amount of screenshots based on the given period and write an animated GIF on the given path(well, right now I didn't create the source yet, hope I will be able to make it work):

package org.jugvale;

import java.io.File; import java.io.IOException; import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.embed.swing.SwingFXUtils; import javafx.event.Event; import javafx.scene.Parent; import javafx.scene.image.WritableImage; import javafx.util.Duration; import javax.imageio.stream.FileImageOutputStream; import javax.imageio.stream.ImageOutputStream;

/** * * @author william */ public class GitExporterFX {

public static void captureNow(Parent target, int durationInMilis, String outputDirectory, int timeBetweenFramesMS, boolean loopContinuously) throws IOException { ImageOutputStream output = new FileImageOutputStream(new File(outputDirectory)); final GifSequenceWriter gifWriter = new GifSequenceWriter(output, 3, timeBetweenFramesMS, loopContinuously); Consumer<Event> run = e -> { int w = (int) target.getBoundsInParent().getWidth(); int h = (int) target.getBoundsInParent().getHeight(); WritableImage img = new WritableImage(w, h); target.snapshot(null, img); try { gifWriter.writeToSequence(SwingFXUtils.fromFXImage(img, null)); } catch (IOException ex) { Logger.getLogger(GitExporterFX.class.getName()).log(Level.SEVERE, null, ex); } };

int keyFrameDuration = durationInMilis / (durationInMilis / timeBetweenFramesMS); final KeyFrame oneFrame = new KeyFrame(Duration.millis(keyFrameDuration), run::accept); Timeline t = new Timeline(durationInMilis, oneFrame); t.setCycleCount(durationInMilis / timeBetweenFramesMS); t.setOnFinished(e -> { try { gifWriter.close(); output.close(); } catch (IOException ex) { Logger.getLogger(GitExporterFX.class.getName()).log(Level.SEVERE, null, ex); } }); t.play(); } }

This code have no great performance since the snapshot method is not "performatic". If we set a small period to collect the GIF's frames, we will face slowness.
That's all, folks! Now you can use the above code in your application to export GIT animations from your JavaFX applications!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK