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).

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

Commit messages:
 - 8388450: ImageIO.write(SwingFXUtils.fromFXImage()) creates 0 length JPEG for 
some images

Changes: https://git.openjdk.org/jfx/pull/2254/files
  Webrev: https://webrevs.openjdk.org/?repo=jfx&pr=2254&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8388450
  Stats: 36 lines in 2 files changed: 25 ins; 4 del; 7 mod
  Patch: https://git.openjdk.org/jfx/pull/2254.diff
  Fetch: git fetch https://git.openjdk.org/jfx.git pull/2254/head:pull/2254

PR: https://git.openjdk.org/jfx/pull/2254

Reply via email to