There's an old bug in JavaFX which prevents you to save images properly using
ImageIO.write(SwingFXUtils.fromFXImage( wi, null), "jpg", new File( fileName1));
The problem occurs when you take a snapshot from a node and try to save it as jpg file. It doesn't occur when you load a jpg into and image and save that image.
It's been a while now and it still isn't fixed. Is there a proper workaround for this without having to use awt?
I know that SwingFXUtils uses awt internally, but having it in your own project feels just wrong.
I checked the bug reports. They were closed with
Not an Issue and Fixed
However, it is an issue and it isn't fixed.
Here's full example code, please change the fileName1 and fileName2 variables to match your path:
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
public class ImageSave extends Application {
String fileName1 = "c:/temp/1.jpg"; // TODO: change filepath
String fileName2 = "c:/temp/2.jpg"; // TODO: change filepath
ImageView imageView;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Image Crop");
BorderPane root = new BorderPane();
Button button = new Button( "Save");
button.setOnAction(e -> save());
root.setTop(button);
// container for image layers
ScrollPane scrollPane = new ScrollPane();
// image layer: a group of images
Group imageLayer = new Group();
// load the image
// Image image = new Image( getClass().getResource( "cat.jpg").toExternalForm());
Image image = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Gatto_europeo4.jpg/1024px-Gatto_europeo4.jpg");
// the container for the image as a javafx node
imageView = new ImageView( image);
// add image to layer
imageLayer.getChildren().add( imageView);
// use scrollpane for image view in case the image is large
scrollPane.setContent(imageLayer);
// put scrollpane in scene
root.setCenter(scrollPane);
primaryStage.setScene(new Scene(root, 1024, 768));
primaryStage.show();
}
private void save() {
SnapshotParameters parameters = new SnapshotParameters();
// parameters.setFill(Color.TRANSPARENT);
WritableImage wi = new WritableImage( (int) imageView.getBoundsInLocal().getWidth(), (int) imageView.getBoundsInLocal().getHeight());
imageView.snapshot(parameters, wi);
// save image
// !!! has bug because of transparency (use approach below) !!!
// --------------------------------
try {
ImageIO.write(SwingFXUtils.fromFXImage( wi, null), "jpg", new File( fileName1));
System.out.println( "Image saved to " + fileName1);
} catch (IOException e) {
e.printStackTrace();
}
// save image (without alpha)
// --------------------------------
BufferedImage bufImageARGB = SwingFXUtils.fromFXImage(wi, null);
BufferedImage bufImageRGB = new BufferedImage(bufImageARGB.getWidth(), bufImageARGB.getHeight(), BufferedImage.OPAQUE);
Graphics2D graphics = bufImageRGB.createGraphics();
graphics.drawImage(bufImageARGB, 0, 0, null);
try {
ImageIO.write(bufImageRGB, "jpg", new File( fileName2));
System.out.println( "Image saved to " + fileName2);
} catch (IOException e) {
e.printStackTrace();
}
graphics.dispose();
}
}
The JavaFX version:
The awt version: