/*
 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.embed.swing;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
import java.nio.IntBuffer;
import javafx.scene.image.Image;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.image.WritablePixelFormat;
import javafx.scene.paint.Color;

This class provides utility methods for converting data types between Swing/AWT and JavaFX formats.
Since:JavaFX 2.2
/** * This class provides utility methods for converting data types between * Swing/AWT and JavaFX formats. * @since JavaFX 2.2 */
public class SwingFXUtils { private SwingFXUtils() {} // no instances
Snapshots the specified BufferedImage and stores a copy of its pixels into a JavaFX Image object, creating a new object if needed. The returned Image will be a static snapshot of the state of the pixels in the BufferedImage at the time the method completes. Further changes to the BufferedImage will not be reflected in the Image.

The optional JavaFX WritableImage parameter may be reused to store the copy of the pixels. A new Image will be created if the supplied object is null, is too small or of a type which the image pixels cannot be easily converted into.

Params:
  • bimg – the BufferedImage object to be converted
  • wimg – an optional WritableImage object that can be used to store the returned pixel data
Returns:an Image object representing a snapshot of the current pixels in the BufferedImage.
Since:JavaFX 2.2
/** * Snapshots the specified {@link BufferedImage} and stores a copy of * its pixels into a JavaFX {@link Image} object, creating a new * object if needed. * The returned {@code Image} will be a static snapshot of the state * of the pixels in the {@code BufferedImage} at the time the method * completes. Further changes to the {@code BufferedImage} will not * be reflected in the {@code Image}. * <p> * The optional JavaFX {@link WritableImage} parameter may be reused * to store the copy of the pixels. * A new {@code Image} will be created if the supplied object is null, * is too small or of a type which the image pixels cannot be easily * converted into. * * @param bimg the {@code BufferedImage} object to be converted * @param wimg an optional {@code WritableImage} object that can be * used to store the returned pixel data * @return an {@code Image} object representing a snapshot of the * current pixels in the {@code BufferedImage}. * @since JavaFX 2.2 */
public static WritableImage toFXImage(BufferedImage bimg, WritableImage wimg) { int bw = bimg.getWidth(); int bh = bimg.getHeight(); switch (bimg.getType()) { case BufferedImage.TYPE_INT_ARGB: case BufferedImage.TYPE_INT_ARGB_PRE: break; default: BufferedImage converted = new BufferedImage(bw, bh, BufferedImage.TYPE_INT_ARGB_PRE); Graphics2D g2d = converted.createGraphics(); g2d.drawImage(bimg, 0, 0, null); g2d.dispose(); bimg = converted; break; } // assert(bimg.getType == TYPE_INT_ARGB[_PRE]); if (wimg != null) { int iw = (int) wimg.getWidth(); int ih = (int) wimg.getHeight(); if (iw < bw || ih < bh) { wimg = null; } else if (bw < iw || bh < ih) { int empty[] = new int[iw]; PixelWriter pw = wimg.getPixelWriter(); PixelFormat<IntBuffer> pf = PixelFormat.getIntArgbPreInstance(); if (bw < iw) { pw.setPixels(bw, 0, iw-bw, bh, pf, empty, 0, 0); } if (bh < ih) { pw.setPixels(0, bh, iw, ih-bh, pf, empty, 0, 0); } } } if (wimg == null) { wimg = new WritableImage(bw, bh); } PixelWriter pw = wimg.getPixelWriter(); DataBufferInt db = (DataBufferInt)bimg.getRaster().getDataBuffer(); int data[] = db.getData(); int offset = bimg.getRaster().getDataBuffer().getOffset(); int scan = 0; SampleModel sm = bimg.getRaster().getSampleModel(); if (sm instanceof SinglePixelPackedSampleModel) { scan = ((SinglePixelPackedSampleModel)sm).getScanlineStride(); } PixelFormat<IntBuffer> pf = (bimg.isAlphaPremultiplied() ? PixelFormat.getIntArgbPreInstance() : PixelFormat.getIntArgbInstance()); pw.setPixels(0, 0, bw, bh, pf, data, offset, scan); return wimg; }
Determine the optimal BufferedImage type to use for the specified fxFormat allowing for the specified bimg to be used as a potential default storage space if it is not null and is compatible.
Params:
  • fxFormat – the PixelFormat of the source FX Image
  • bimg – an optional existing BufferedImage to be used for storage if it is compatible, or null
Returns:
/** * Determine the optimal BufferedImage type to use for the specified * {@code fxFormat} allowing for the specified {@code bimg} to be used * as a potential default storage space if it is not null and is compatible. * * @param fxFormat the PixelFormat of the source FX Image * @param bimg an optional existing {@code BufferedImage} to be used * for storage if it is compatible, or null * @return */
static int getBestBufferedImageType(PixelFormat<?> fxFormat, BufferedImage bimg, boolean isOpaque) { if (bimg != null) { int bimgType = bimg.getType(); if (bimgType == BufferedImage.TYPE_INT_ARGB || bimgType == BufferedImage.TYPE_INT_ARGB_PRE || (isOpaque && (bimgType == BufferedImage.TYPE_INT_BGR || bimgType == BufferedImage.TYPE_INT_RGB))) { // We will allow the caller to give us a BufferedImage // that has an alpha channel, but we might not otherwise // construct one ourselves. // We will also allow them to choose their own premultiply // type which may not match the image. // If left to our own devices we might choose a more specific // format as indicated by the choices below. return bimgType; } } switch (fxFormat.getType()) { default: case BYTE_BGRA_PRE: case INT_ARGB_PRE: return BufferedImage.TYPE_INT_ARGB_PRE; case BYTE_BGRA: case INT_ARGB: return BufferedImage.TYPE_INT_ARGB; case BYTE_RGB: return BufferedImage.TYPE_INT_RGB; case BYTE_INDEXED: return (fxFormat.isPremultiplied() ? BufferedImage.TYPE_INT_ARGB_PRE : BufferedImage.TYPE_INT_ARGB); } }
Determine the appropriate WritablePixelFormat type that can be used to transfer data into the indicated BufferedImage.
Params:
  • bimg – the BufferedImage that will be used as a destination for a PixelReader<IntBuffer>#getPixels() operation.
Returns:
/** * Determine the appropriate {@link WritablePixelFormat} type that can * be used to transfer data into the indicated BufferedImage. * * @param bimg the BufferedImage that will be used as a destination for * a {@code PixelReader<IntBuffer>#getPixels()} operation. * @return */
private static WritablePixelFormat<IntBuffer> getAssociatedPixelFormat(BufferedImage bimg) { switch (bimg.getType()) { // We lie here for xRGB, but we vetted that the src data was opaque // so we can ignore the alpha. We use ArgbPre instead of Argb // just to get a loop that does not have divides in it if the // PixelReader happens to not know the data is opaque. case BufferedImage.TYPE_INT_RGB: case BufferedImage.TYPE_INT_ARGB_PRE: return PixelFormat.getIntArgbPreInstance(); case BufferedImage.TYPE_INT_ARGB: return PixelFormat.getIntArgbInstance(); default: // Should not happen... throw new InternalError("Failed to validate BufferedImage type"); } } private static boolean checkFXImageOpaque(PixelReader pr, int iw, int ih) { for (int x = 0; x < iw; x++) { for (int y = 0; y < ih; y++) { Color color = pr.getColor(x,y); if (color.getOpacity() != 1.0) { return false; } } } return true; }
Snapshots the specified JavaFX Image object and stores a copy of its pixels into a BufferedImage object, creating a new object if needed. The method will only convert a JavaFX Image that is readable as per the conditions on the Image.getPixelReader() method. If the Image is not readable, as determined by its getPixelReader() method, then this method will return null. If the Image is a writable, or other dynamic image, then the BufferedImage will only be set to the current state of the pixels in the image as determined by its PixelReader. Further changes to the pixels of the Image will not be reflected in the returned BufferedImage.

The optional BufferedImage parameter may be reused to store the copy of the pixels. A new BufferedImage will be created if the supplied object is null, is too small or of a type which the image pixels cannot be easily converted into.

Params:
  • img – the JavaFX Image to be converted
  • bimg – an optional BufferedImage object that may be used to store the returned pixel data
Returns:a BufferedImage containing a snapshot of the JavaFX Image, or null if the Image is not readable.
Since:JavaFX 2.2
/** * Snapshots the specified JavaFX {@link Image} object and stores a * copy of its pixels into a {@link BufferedImage} object, creating * a new object if needed. * The method will only convert a JavaFX {@code Image} that is readable * as per the conditions on the * {@link Image#getPixelReader() Image.getPixelReader()} * method. * If the {@code Image} is not readable, as determined by its * {@code getPixelReader()} method, then this method will return null. * If the {@code Image} is a writable, or other dynamic image, then * the {@code BufferedImage} will only be set to the current state of * the pixels in the image as determined by its {@link PixelReader}. * Further changes to the pixels of the {@code Image} will not be * reflected in the returned {@code BufferedImage}. * <p> * The optional {@code BufferedImage} parameter may be reused to store * the copy of the pixels. * A new {@code BufferedImage} will be created if the supplied object * is null, is too small or of a type which the image pixels cannot * be easily converted into. * * @param img the JavaFX {@code Image} to be converted * @param bimg an optional {@code BufferedImage} object that may be * used to store the returned pixel data * @return a {@code BufferedImage} containing a snapshot of the JavaFX * {@code Image}, or null if the {@code Image} is not readable. * @since JavaFX 2.2 */
public static BufferedImage fromFXImage(Image img, BufferedImage bimg) { PixelReader pr = img.getPixelReader(); if (pr == null) { return null; } int iw = (int) img.getWidth(); int ih = (int) img.getHeight(); PixelFormat<?> fxFormat = pr.getPixelFormat(); boolean srcPixelsAreOpaque = false; switch (fxFormat.getType()) { case INT_ARGB_PRE: case INT_ARGB: case BYTE_BGRA_PRE: case BYTE_BGRA: // Check fx image opacity only if // supplied BufferedImage is without alpha channel if (bimg != null && (bimg.getType() == BufferedImage.TYPE_INT_BGR || bimg.getType() == BufferedImage.TYPE_INT_RGB)) { srcPixelsAreOpaque = checkFXImageOpaque(pr, iw, ih); } break; case BYTE_RGB: srcPixelsAreOpaque = true; break; } int prefBimgType = getBestBufferedImageType(pr.getPixelFormat(), bimg, srcPixelsAreOpaque); if (bimg != null) { int bw = bimg.getWidth(); int bh = bimg.getHeight(); if (bw < iw || bh < ih || bimg.getType() != prefBimgType) { bimg = null; } else if (iw < bw || ih < bh) { Graphics2D g2d = bimg.createGraphics(); g2d.setComposite(AlphaComposite.Clear); g2d.fillRect(0, 0, bw, bh); g2d.dispose(); } } if (bimg == null) { bimg = new BufferedImage(iw, ih, prefBimgType); } DataBufferInt db = (DataBufferInt)bimg.getRaster().getDataBuffer(); int data[] = db.getData(); int offset = bimg.getRaster().getDataBuffer().getOffset(); int scan = 0; SampleModel sm = bimg.getRaster().getSampleModel(); if (sm instanceof SinglePixelPackedSampleModel) { scan = ((SinglePixelPackedSampleModel)sm).getScanlineStride(); } WritablePixelFormat<IntBuffer> pf = getAssociatedPixelFormat(bimg); pr.getPixels(0, 0, iw, ih, pf, data, offset, scan); return bimg; } }