On Mon, 10 Aug 2026 08:54:30 GMT, Prasanta Sadhukhan <[email protected]> 
wrote:

> If an image has RGBA encoding and all pixels are opaque, then 
> `SwingFXUtils.fromFXImage(image, null)` selects TYPE_INT_ARGB_PRE pixel 
> format but the JPEG ImageIO writer cannot encode this alpha-bearing image 
> pixel format since it has no support for it and returns false; so an empty 
> byte array is created.
> 
> Issue is when `bimg` (used to store the returned pixel data from 
> `fromFXImage`) is null`,  fromFXImage` uses the JavaFX PixelReader’s storage 
> format, not the alpha values of individual pixels. 
> For an RGBA PNG, JavaFX commonly decodes the pixels into premultiplied 
> ARGB/BGRA, so `fxFormat.getType()` is one of INT_ARGB_PRE/BYTE_BGRA_PRE so 
> `getBestBufferedImageType` returns `BufferedImage.TYPE_INT_ARGB_PRE` because 
> the pixel format has an alpha component. It does not inspect whether every 
> alpha value happens to be 255.
> 
> The existing `fromFXImage` code only calls `checkFXImageOpaque()` when the 
> caller supplies a non-null `bImg` BufferedImage. 
> If `bImg `is null, all-opaque RGBA PNG produces an alpha-capable 
> BufferedImage i.e., BufferedImage.TYPE_INT_ARGB_PRE
> 
> if (bimg == null) {
>     bimg = new BufferedImage(iw, ih, prefBimgType);
> }
> 
> and since JPEG has no standard alpha channel so it cannot store transparency, 
> so when `ImageIO.write(image, "jpg", out)` runs, ImageIO looks for a 
> registered JPEG writer which can encode that pre-multiplied-alpha image type,
>  but the JPEG writer rejects an alpha-bearing BufferedImage, so 
> `ImageIO.write` finds no suitable writer and returns false 
> so OutputStream is not written into and have 0 bytes 
> [Basically the JPEG writer does not check whether the alpha values are all 
> 255; it only sees that the input image has an alpha channel and declines to 
> write it]
> 
> The proposed JavaFX change avoids the rejection for an RGBA-formatted but 
> fully opaque image by returning TYPE_INT_RGB, which JPEG can encode. 
> ie., for a JavaFX image with an alpha-capable format but only opaque pixels, 
> `fromFXImage(image, null)` is made to choose RGB format rather than ARGB.
> Additionally, checkFXImageOpaque is improved to scan one row at a time 
> instead of costly full-image scan so that unnecessary Color object for each 
> pixels is not created just to inspect alpha.
> 
> A regression subtest is added to existing testcase
> 
> ---------
> - [x] I confirm that I make this contribution in accordance with the [OpenJDK 
> Interim AI Policy](https://openjdk.org/legal/ai).

modules/javafx.swing/src/main/java/javafx/embed/swing/SwingFXUtils.java line 
209:

> 207:         int[] pixels = new int[iw];
> 208:         WritablePixelFormat<IntBuffer> format =
> 209:                 PixelFormat.getIntArgbPreInstance();

just curious: why is this line broken?  it fits in 120 columns just fine.  time 
to update the formatting rules?

modules/javafx.swing/src/main/java/javafx/embed/swing/SwingFXUtils.java line 
213:

> 211:             pr.getPixels(0, y, iw, 1, format, pixels, 0, iw);
> 212:             for (int pixel : pixels) {
> 213:                 if ((pixel >>> 24) != 0xff) {

I would have done
`((pixel & 0xff000000) != 0xff000000)`

but I think there is no difference in performance whatsoever

modules/javafx.swing/src/main/java/javafx/embed/swing/SwingFXUtils.java line 
259:

> 257:         PixelFormat<?> fxFormat = pr.getPixelFormat();
> 258:         boolean srcPixelsAreOpaque = false;
> 259:         boolean opacityMatters = bimg == null ||

minor: this is calculated even when it's not needed.  could it be moved to L267?

modules/javafx.swing/src/main/java/javafx/embed/swing/SwingFXUtils.java line 
266:

> 264:             case INT_ARGB:
> 265:             case BYTE_BGRA_PRE:
> 266:             case BYTE_INDEXED:

question: this switch statement is missing `BYTE_BGRA`.  is this a problem?

tests/system/src/test/java/test/javafx/embed/swing/SwingFXUtilsTest.java line 
89:

> 87:     @Test
> 88:     public void testOpaqueArgbImageCanBeWrittenAsJpeg() throws Exception {
> 89:         WritableImage image = new WritableImage(2, 1);

would it make sense to iterate over every `PixelFormat.Type` using 
`WritableImage(PixelBuffer)` constructor  to make sure we are getting a 
meaningful result in each case?

-------------

PR Review Comment: https://git.openjdk.org/jfx/pull/2254#discussion_r3753259163
PR Review Comment: https://git.openjdk.org/jfx/pull/2254#discussion_r3753246858
PR Review Comment: https://git.openjdk.org/jfx/pull/2254#discussion_r3753276434
PR Review Comment: https://git.openjdk.org/jfx/pull/2254#discussion_r3753288191
PR Review Comment: https://git.openjdk.org/jfx/pull/2254#discussion_r3753383492

Reply via email to