11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javafx.embed.swing;
27
28 import java.awt.AlphaComposite;
29 import java.awt.Graphics2D;
30 import java.awt.image.BufferedImage;
31 import java.nio.IntBuffer;
32 import java.util.Set;
33 import java.util.HashSet;
34 import javafx.application.Platform;
35 import javafx.scene.image.Image;
36 import javafx.scene.image.PixelFormat;
37 import javafx.scene.image.PixelReader;
38 import javafx.scene.image.PixelWriter;
39 import javafx.scene.image.WritableImage;
40 import javafx.scene.image.WritablePixelFormat;
41 import javafx.scene.paint.Color;
42 import com.sun.javafx.tk.Toolkit;
43 import javax.swing.SwingUtilities;
44 import sun.awt.image.IntegerComponentRaster;
45
46 /**
47 * This class provides utility methods for converting data types between
48 * Swing/AWT and JavaFX formats.
49 * @since JavaFX 2.2
50 */
51 public class SwingFXUtils {
52 private SwingFXUtils() {} // no instances
53
54 /**
55 * Snapshots the specified {@link BufferedImage} and stores a copy of
56 * its pixels into a JavaFX {@link Image} object, creating a new
57 * object if needed.
58 * The returned {@code Image} will be a static snapshot of the state
59 * of the pixels in the {@code BufferedImage} at the time the method
60 * completes. Further changes to the {@code BufferedImage} will not
61 * be reflected in the {@code Image}.
62 * <p>
63 * The optional JavaFX {@link WritableImage} parameter may be reused
64 * to store the copy of the pixels.
94 int iw = (int) wimg.getWidth();
95 int ih = (int) wimg.getHeight();
96 if (iw < bw || ih < bh) {
97 wimg = null;
98 } else if (bw < iw || bh < ih) {
99 int empty[] = new int[iw];
100 PixelWriter pw = wimg.getPixelWriter();
101 PixelFormat<IntBuffer> pf = PixelFormat.getIntArgbPreInstance();
102 if (bw < iw) {
103 pw.setPixels(bw, 0, iw-bw, bh, pf, empty, 0, 0);
104 }
105 if (bh < ih) {
106 pw.setPixels(0, bh, iw, ih-bh, pf, empty, 0, 0);
107 }
108 }
109 }
110 if (wimg == null) {
111 wimg = new WritableImage(bw, bh);
112 }
113 PixelWriter pw = wimg.getPixelWriter();
114 IntegerComponentRaster icr = (IntegerComponentRaster) bimg.getRaster();
115 int data[] = icr.getDataStorage();
116 int offset = icr.getDataOffset(0);
117 int scan = icr.getScanlineStride();
118 PixelFormat<IntBuffer> pf = (bimg.isAlphaPremultiplied() ?
119 PixelFormat.getIntArgbPreInstance() :
120 PixelFormat.getIntArgbInstance());
121 pw.setPixels(0, 0, bw, bh, pf, data, offset, scan);
122 return wimg;
123 }
124
125 /**
126 * Determine the optimal BufferedImage type to use for the specified
127 * {@code fxFormat} allowing for the specified {@code bimg} to be used
128 * as a potential default storage space if it is not null and is compatible.
129 *
130 * @param fxFormat the PixelFormat of the source FX Image
131 * @param bimg an optional existing {@code BufferedImage} to be used
132 * for storage if it is compatible, or null
133 * @return
134 */
135 static int
136 getBestBufferedImageType(PixelFormat<?> fxFormat, BufferedImage bimg,
137 boolean isOpaque)
264 case BYTE_RGB:
265 srcPixelsAreOpaque = true;
266 break;
267 }
268 int prefBimgType = getBestBufferedImageType(pr.getPixelFormat(), bimg, srcPixelsAreOpaque);
269 if (bimg != null) {
270 int bw = bimg.getWidth();
271 int bh = bimg.getHeight();
272 if (bw < iw || bh < ih || bimg.getType() != prefBimgType) {
273 bimg = null;
274 } else if (iw < bw || ih < bh) {
275 Graphics2D g2d = bimg.createGraphics();
276 g2d.setComposite(AlphaComposite.Clear);
277 g2d.fillRect(0, 0, bw, bh);
278 g2d.dispose();
279 }
280 }
281 if (bimg == null) {
282 bimg = new BufferedImage(iw, ih, prefBimgType);
283 }
284 IntegerComponentRaster icr = (IntegerComponentRaster) bimg.getRaster();
285 int offset = icr.getDataOffset(0);
286 int scan = icr.getScanlineStride();
287 int data[] = icr.getDataStorage();
288 WritablePixelFormat<IntBuffer> pf = getAssociatedPixelFormat(bimg);
289 pr.getPixels(0, 0, iw, ih, pf, data, offset, scan);
290 return bimg;
291 }
292
293 /**
294 * If called from the FX Application Thread
295 * invokes a runnable directly blocking the calling code
296 * Otherwise
297 * uses Platform.runLater without blocking
298 */
299 static void runOnFxThread(Runnable runnable) {
300 if (Platform.isFxApplicationThread()) {
301 runnable.run();
302 } else {
303 Platform.runLater(runnable);
304 }
305 }
306
307 /**
308 * If called from the event dispatch thread
309 * invokes a runnable directly blocking the calling code
310 * Otherwise
311 * uses SwingUtilities.invokeLater without blocking
312 */
313 static void runOnEDT(final Runnable r) {
314 if (SwingUtilities.isEventDispatchThread()) {
315 r.run();
316 } else {
317 SwingUtilities.invokeLater(r);
318 }
319 }
320
321 private static final Set<Object> eventLoopKeys = new HashSet<>();
322
323 /**
324 * The runnable is responsible for leaving the nested event loop.
325 */
326 static void runOnEDTAndWait(Object nestedLoopKey, Runnable r) {
327 Toolkit.getToolkit().checkFxUserThread();
328
329 if (SwingUtilities.isEventDispatchThread()) {
330 r.run();
331 } else {
332 eventLoopKeys.add(nestedLoopKey);
333 SwingUtilities.invokeLater(r);
334 Toolkit.getToolkit().enterNestedEventLoop(nestedLoopKey);
335 }
336 }
337
338 static void leaveFXNestedLoop(Object nestedLoopKey) {
339 if (!eventLoopKeys.contains(nestedLoopKey)) return;
340
341 if (Platform.isFxApplicationThread()) {
342 Toolkit.getToolkit().exitNestedEventLoop(nestedLoopKey, null);
343 } else {
344 Platform.runLater(() -> {
345 Toolkit.getToolkit().exitNestedEventLoop(nestedLoopKey, null);
346 });
347 }
348
349 eventLoopKeys.remove(nestedLoopKey);
350 }
351
352 }
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javafx.embed.swing;
27
28 import java.awt.AlphaComposite;
29 import java.awt.Graphics2D;
30 import java.awt.image.BufferedImage;
31 import java.awt.image.DataBufferInt;
32 import java.awt.image.SampleModel;
33 import java.awt.image.SinglePixelPackedSampleModel;
34 import java.nio.IntBuffer;
35 import java.util.Set;
36 import java.util.HashSet;
37 import javafx.application.Platform;
38 import javafx.scene.image.Image;
39 import javafx.scene.image.PixelFormat;
40 import javafx.scene.image.PixelReader;
41 import javafx.scene.image.PixelWriter;
42 import javafx.scene.image.WritableImage;
43 import javafx.scene.image.WritablePixelFormat;
44 import javafx.scene.paint.Color;
45 import com.sun.javafx.tk.Toolkit;
46 import javax.swing.SwingUtilities;
47
48 /**
49 * This class provides utility methods for converting data types between
50 * Swing/AWT and JavaFX formats.
51 * @since JavaFX 2.2
52 */
53 public class SwingFXUtils {
54 private SwingFXUtils() {} // no instances
55
56 /**
57 * Snapshots the specified {@link BufferedImage} and stores a copy of
58 * its pixels into a JavaFX {@link Image} object, creating a new
59 * object if needed.
60 * The returned {@code Image} will be a static snapshot of the state
61 * of the pixels in the {@code BufferedImage} at the time the method
62 * completes. Further changes to the {@code BufferedImage} will not
63 * be reflected in the {@code Image}.
64 * <p>
65 * The optional JavaFX {@link WritableImage} parameter may be reused
66 * to store the copy of the pixels.
96 int iw = (int) wimg.getWidth();
97 int ih = (int) wimg.getHeight();
98 if (iw < bw || ih < bh) {
99 wimg = null;
100 } else if (bw < iw || bh < ih) {
101 int empty[] = new int[iw];
102 PixelWriter pw = wimg.getPixelWriter();
103 PixelFormat<IntBuffer> pf = PixelFormat.getIntArgbPreInstance();
104 if (bw < iw) {
105 pw.setPixels(bw, 0, iw-bw, bh, pf, empty, 0, 0);
106 }
107 if (bh < ih) {
108 pw.setPixels(0, bh, iw, ih-bh, pf, empty, 0, 0);
109 }
110 }
111 }
112 if (wimg == null) {
113 wimg = new WritableImage(bw, bh);
114 }
115 PixelWriter pw = wimg.getPixelWriter();
116 int data[];
117 DataBufferInt db = (DataBufferInt)bimg.getRaster().getDataBuffer();
118 data = db.getData();
119 int offset = bimg.getRaster().getDataBuffer().getOffset();
120 int scan = 0;
121 SampleModel sm = bimg.getRaster().getSampleModel();
122 if (sm instanceof SinglePixelPackedSampleModel) {
123 scan = ((SinglePixelPackedSampleModel)sm).getScanlineStride();
124 }
125
126 PixelFormat<IntBuffer> pf = (bimg.isAlphaPremultiplied() ?
127 PixelFormat.getIntArgbPreInstance() :
128 PixelFormat.getIntArgbInstance());
129 pw.setPixels(0, 0, bw, bh, pf, data, offset, scan);
130 return wimg;
131 }
132
133 /**
134 * Determine the optimal BufferedImage type to use for the specified
135 * {@code fxFormat} allowing for the specified {@code bimg} to be used
136 * as a potential default storage space if it is not null and is compatible.
137 *
138 * @param fxFormat the PixelFormat of the source FX Image
139 * @param bimg an optional existing {@code BufferedImage} to be used
140 * for storage if it is compatible, or null
141 * @return
142 */
143 static int
144 getBestBufferedImageType(PixelFormat<?> fxFormat, BufferedImage bimg,
145 boolean isOpaque)
272 case BYTE_RGB:
273 srcPixelsAreOpaque = true;
274 break;
275 }
276 int prefBimgType = getBestBufferedImageType(pr.getPixelFormat(), bimg, srcPixelsAreOpaque);
277 if (bimg != null) {
278 int bw = bimg.getWidth();
279 int bh = bimg.getHeight();
280 if (bw < iw || bh < ih || bimg.getType() != prefBimgType) {
281 bimg = null;
282 } else if (iw < bw || ih < bh) {
283 Graphics2D g2d = bimg.createGraphics();
284 g2d.setComposite(AlphaComposite.Clear);
285 g2d.fillRect(0, 0, bw, bh);
286 g2d.dispose();
287 }
288 }
289 if (bimg == null) {
290 bimg = new BufferedImage(iw, ih, prefBimgType);
291 }
292 DataBufferInt db = (DataBufferInt)bimg.getRaster().getDataBuffer();
293 int data[] = db.getData();
294 int offset = bimg.getRaster().getDataBuffer().getOffset();
295 int scan = 0;
296 SampleModel sm = bimg.getRaster().getSampleModel();
297 if (sm instanceof SinglePixelPackedSampleModel) {
298 scan = ((SinglePixelPackedSampleModel)sm).getScanlineStride();
299 }
300
301 WritablePixelFormat<IntBuffer> pf = getAssociatedPixelFormat(bimg);
302 pr.getPixels(0, 0, iw, ih, pf, data, offset, scan);
303 return bimg;
304 }
305
306 /**
307 * If called from the FX Application Thread
308 * invokes a runnable directly blocking the calling code
309 * Otherwise
310 * uses Platform.runLater without blocking
311 *
312 * @param runnable {@code Runnable} to be invoked
313 */
314 public static void runOnFxThread(Runnable runnable) {
315 if (Platform.isFxApplicationThread()) {
316 runnable.run();
317 } else {
318 Platform.runLater(runnable);
319 }
320 }
321
322 /**
323 * If called from the event dispatch thread
324 * invokes a runnable directly blocking the calling code
325 * Otherwise
326 * uses SwingUtilities.invokeLater without blocking
327 *
328 * @param r {@code Runnable} to be invoked
329 */
330 public static void runOnEDT(final Runnable r) {
331 if (SwingUtilities.isEventDispatchThread()) {
332 r.run();
333 } else {
334 SwingUtilities.invokeLater(r);
335 }
336 }
337
338 private static final Set<Object> eventLoopKeys = new HashSet<>();
339
340 /**
341 * The runnable is responsible for entering the nested event loop.
342 *
343 * @param nestedLoopKey the Object that identifies the nested event loop,
344 * which must not be null
345 * @param r {@code Runnable} to be invoked
346 */
347 public static void runOnEDTAndWait(Object nestedLoopKey, Runnable r) {
348 Toolkit.getToolkit().checkFxUserThread();
349
350 if (SwingUtilities.isEventDispatchThread()) {
351 r.run();
352 } else {
353 eventLoopKeys.add(nestedLoopKey);
354 SwingUtilities.invokeLater(r);
355 Toolkit.getToolkit().enterNestedEventLoop(nestedLoopKey);
356 }
357 }
358
359 /**
360 * The runnable is responsible for leaving the nested event loop.
361 *
362 * @param nestedLoopKey the Object that identifies the nested event loop,
363 * which must not be null
364 */
365 public static void leaveFXNestedLoop(Object nestedLoopKey) {
366 if (!eventLoopKeys.contains(nestedLoopKey)) return;
367
368 if (Platform.isFxApplicationThread()) {
369 Toolkit.getToolkit().exitNestedEventLoop(nestedLoopKey, null);
370 } else {
371 Platform.runLater(() -> {
372 Toolkit.getToolkit().exitNestedEventLoop(nestedLoopKey, null);
373 });
374 }
375
376 eventLoopKeys.remove(nestedLoopKey);
377 }
378
379 }
|