How would I go about writing a javafx.scene.image.Image image to a file. I know you can use ImageIO on BufferedImages but is there any way to do it with a javafx Image?
3 Answers
Just convert it to a BufferedImage
first, using javafx.embed.swing.SwingFXUtils
:
Image image = ... ; // javafx.scene.image.Image
String format = ... ;
File file = ... ;
ImageIO.write(SwingFXUtils.fromFXImage(image, null), format, file);
-
You don't need to use Java Swing to convert an Image to a BufferedImage. See my answer for example: https://stackoverflow.com/questions/21540378/convert-javafx-image-to-bufferedimage/72114722#72114722 – Luna May 04 '22 at 14:30
-
@Luna That still uses java.awt, which requires the same module as Swing, unless I'm missing something obvious. – James_D May 04 '22 at 15:20
Almost 3 years later and I now have the knowledge to do and answer this. Yes the original answer was also valid but it involved first converting the image to a BufferedImage and I ideally wanted to avoid swing entirely. While this does output the raw RGBA version of the image that's good enough for what I needed to do. I actually could just use raw BGRA since I was writing the software to open the result but since gimp can't open that I figure I'd convert it to RGBA.
Image img = new Image("file:test.png");
int width = (int) img.getWidth();
int height = (int) img.getHeight();
PixelReader reader = img.getPixelReader();
byte[] buffer = new byte[width * height * 4];
WritablePixelFormat<ByteBuffer> format = PixelFormat.getByteBgraInstance();
reader.getPixels(0, 0, width, height, format, buffer, 0, width * 4);
try {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("test.data"));
for(int count = 0; count < buffer.length; count += 4) {
out.write(buffer[count + 2]);
out.write(buffer[count + 1]);
out.write(buffer[count]);
out.write(buffer[count + 3]);
}
out.flush();
out.close();
} catch(IOException e) {
e.printStackTrace();
}

- 987
- 1
- 9
- 22
-
5@Cypher Each color value is 0-255(1 byte) and you have 4 of them per pixel: Blue, green, red, and alpha(transparent) – Scoopta Jul 03 '17 at 19:20
-
Is this valid with every image format? what if the image doesn't have transparency like jpg? – JFValdes Nov 05 '18 at 17:47
-
1@JFValdes I have not tried it with a JPEG however my guess would be that since the pixel format is acquired using `PixelFormat.getByteBgraInstance()` it will always have an alpha channel. My assumption is that in the case of JPEG all the alpha values will be 0xFF. I can test it when I have time and get back to you assuming you haven't already. – Scoopta Nov 05 '18 at 17:55
-
1@JFValdes yes, it works just fine with jpeg. As I suspected the alpha values all end up as 0xFF. – Scoopta Nov 05 '18 at 22:19
-
1Careful, I don't believe this approach will work with animated GIFs as I think `getPixelReader()` returns `null` in that case. – Slaw Aug 31 '21 at 19:33
-
^^ Not to mention I'm not sure how you could store an animated image in raw RGB format. – Slaw Sep 01 '21 at 01:14
-
1@Slaw in a raw format 3rd party software can open? Probably can't, in your own software which was my original goal anyway? For sure can, not that my original intention even cared about gifs to begin with though. – Scoopta Aug 14 '22 at 06:42
-
I believe my concerns about storing an animated image in "raw RGB format" is that such a format could not convey the necessary information. You need to know when one frame ends, and another begins. And you need to know the delay between frames. Possibly other information as well. You could add this information to the byte sequence, but then it's not really raw RGB data and is instead your own "image file" format. You might as well just keep the GIF file, as that's the only animated format that JavaFX supports (as far as I'm aware). – Slaw Aug 14 '22 at 06:50
-
Besides, other than loading a GIF file, I don't think you can just load an animated image directly into a JavaFX `Image`. Sticking purely in JavaFX, you'd need your own animation (see `javafx.animation`) that either progresses through multiple `Image` instances, one for each frame, or updates a single `WritableImage` instance periodically. But maybe you could do something with `BufferedImage` and related API to create an animated image (e.g., manually "build" a GIF?), and then convert it to a JavaFX image with `SwingFXUtils`. – Slaw Aug 14 '22 at 06:54
-
@Slaw well anything swing related was off the table to begin with, that was the whole point of doing it this way and not just using SwingFX and ImageIO. Also raw images are trivial to separate into frames since the dimensions must be provided by the user and the dimensions correspond directly to a number of bytes, each frame would just be concatenated together. However, I do think you might be right about being unable to display in it JFX. – Scoopta Aug 14 '22 at 06:57
-
Ah yes, you're right about separating frames. You still have the frame duration problem, unless you're okay with using a default duration or having the user specify the duration as well (or saving it somewhere else). Regardless, coming up with solutions to these potential problems is likely futile, given `getPixelReader()` returns `null` for animated images. My comments were merely meant to forewarn anyone who came across this answer and needed to save animated images as well as static ones. – Slaw Aug 14 '22 at 07:13
-
@Slaw Yep, fair enough...although I wasn't actually aware of that behavior...does that mean there's no way to read the data from an animated image – Scoopta Aug 14 '22 at 07:16
-
The documentation states: "_This method returns a PixelReader that provides access to read the pixels of the image, if the image is readable. If this method returns null then this image does not support reading at this time. This method will return null if the image is being loaded from a source and is still incomplete {the progress is still <1.0) or if there was an error. This method may also return null for some images in a format that is not supported for reading and writing pixels to._" – Slaw Aug 14 '22 at 07:20
-
It's that last sentence that's important in this context. In my experience, I've only had that occur with GIFs, and never have I had it _not_ return `null` for a GIF. As for other alternatives, ignoring `SwingFXUtils` and such, the only thing I can think of is to snapshot the `ImageView` for each frame somehow, but I would guess that could potentially mess up the quality at least, if not the coloring as well. – Slaw Aug 14 '22 at 07:22
-
@Slaw interesting, well good to know. I'll keep that in mind for the future if I ever go back to using JavaFX. Also I feel like a screenshot would be less than ideal so I suppose for gifs you really only have one choice and that's to add swing as an extra dependency. – Scoopta Aug 15 '22 at 17:48
-
Thing is, the `javafx.base` module [already requires](https://github.com/openjdk/jfx/blob/master/modules/javafx.base/src/main/java/module-info.java#L34) the `java.desktop` module (not transitively, but still). And Swing (and AWT) is in the `java.desktop` module. So, any JavaFX application already includes AWT/Swing. I don't understand why it was designed this way. – Slaw Aug 15 '22 at 18:13
-
@Slaw that actually makes me a little angry, I was not aware of that and that largely makes all this kind of pointless. So much for Oracle's vision of JFX replacing swing...ofc that was never going to happen after they stopped distributing it with the JDK anyway. I don't use JFX anymore as it doesn't support wayland which makes it borderline useless to me but that's really disappointing all the same. – Scoopta Aug 15 '22 at 18:34
JavaFX has no built-in method to do this.
To solve this problem, I implemented a very small (< 20KiB) library for writing PNG files: https://github.com/Glavo/SimplePNG
Usage:
Image img = new Image("path-to-image.jpg");
try (PNGWriter writer = new PNGWriter(Files.newOutputStream(Path.of("output.png")))) {
writer.write(PNGJavaFXUtils.asArgbImage(img));
}
// Or you can use the shortcut:
// PNGJavaFXUtils.writeImage(img, Path.of("output.png"));
It has no dependencies and can work on the JRE that only have java.base
.
I avoid the dependence on Java AWT (java.desktop
) through it.

- 448
- 3
- 13
-
Ironically I actually ended up doing the same although I moved away from JFX anyway. – Scoopta Jan 13 '23 at 18:48