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.scene;
27
28 import com.sun.glass.ui.Application;
29 import com.sun.glass.ui.Accessible;
30 import com.sun.javafx.util.Logging;
31 import com.sun.javafx.util.Utils;
32 import com.sun.javafx.application.PlatformImpl;
33 import com.sun.javafx.collections.TrackableObservableList;
34 import com.sun.javafx.css.StyleManager;
35 import com.sun.javafx.cursor.CursorFrame;
36 import com.sun.javafx.event.EventQueue;
37 import com.sun.javafx.geom.PickRay;
38 import com.sun.javafx.geom.Vec3d;
39 import com.sun.javafx.geom.transform.BaseTransform;
40 import com.sun.javafx.perf.PerformanceTracker;
41 import com.sun.javafx.runtime.SystemProperties;
42 import com.sun.javafx.scene.CssFlags;
43 import com.sun.javafx.scene.LayoutFlags;
44 import com.sun.javafx.scene.SceneEventDispatcher;
45 import com.sun.javafx.scene.SceneHelper;
46 import com.sun.javafx.scene.input.DragboardHelper;
47 import com.sun.javafx.scene.input.ExtendedInputMethodRequests;
48 import com.sun.javafx.scene.input.InputEventUtils;
49 import com.sun.javafx.scene.input.PickResultChooser;
50 import com.sun.javafx.scene.traversal.Direction;
51 import com.sun.javafx.scene.traversal.SceneTraversalEngine;
52 import com.sun.javafx.scene.traversal.TopMostTraversalEngine;
53 import com.sun.javafx.sg.prism.NGCamera;
54 import com.sun.javafx.sg.prism.NGLightBase;
55 import com.sun.javafx.tk.*;
56 import com.sun.prism.impl.PrismSettings;
57
58 import javafx.animation.KeyFrame;
59 import javafx.animation.Timeline;
60 import javafx.application.ConditionalFeature;
61 import javafx.application.Platform;
393 public void preferredSize(Scene scene) {
394 scene.preferredSize();
395 }
396
397 @Override
398 public void disposePeer(Scene scene) {
399 scene.disposePeer();
400 }
401
402 @Override
403 public void initPeer(Scene scene) {
404 scene.initPeer();
405 }
406
407 @Override
408 public void setWindow(Scene scene, Window window) {
409 scene.setWindow(window);
410 }
411
412 @Override
413 public TKPulseListener getScenePulseListener(Scene scene) {
414 return scene.getScenePulseListener();
415 }
416
417 @Override
418 public TKScene getPeer(Scene scene) {
419 return scene.getPeer();
420 }
421
422 @Override
423 public void setAllowPGAccess(boolean flag) {
424 Scene.setAllowPGAccess(flag);
425 }
426
427 @Override
428 public void setPaused(boolean paused) {
429 Scene.paused = paused;
430 }
431
432 @Override
433 public void parentEffectiveOrientationInvalidated(
434 final Scene scene) {
435 scene.parentEffectiveOrientationInvalidated();
436 }
437
438 @Override
439 public Camera getEffectiveCamera(Scene scene) {
440 return scene.getEffectiveCamera();
441 }
442
443 @Override
444 public Scene createPopupScene(Parent root) {
445 return new Scene(root) {
446 @Override
447 void doLayoutPass() {
448 resizeRootToPreferredSize(getRoot());
449 super.doLayoutPass();
450 }
451
452 @Override
464 scene.transientFocusContainer = node;
465 }
466 }
467
468 @Override
469 public Accessible getAccessible(Scene scene) {
470 return scene.getAccessible();
471 }
472 });
473 }
474
475 // Reserve space for 30 nodes in the dirtyNodes set.
476 private static final int MIN_DIRTY_CAPACITY = 30;
477
478 // For debugging
479 private static boolean inSynchronizer = false;
480 private static boolean inMousePick = false;
481 private static boolean allowPGAccess = false;
482 private static int pgAccessCount = 0;
483
484 // Flag set by the Toolkit when we are paused for JMX debugging
485 private static boolean paused = false;
486
487 /**
488 * Used for debugging purposes. Returns true if we are in either the
489 * mouse event code (picking) or the synchronizer, or if the scene is
490 * not yet initialized,
491 *
492 */
493 static boolean isPGAccessAllowed() {
494 return inSynchronizer || inMousePick || allowPGAccess;
495 }
496
497 static void setAllowPGAccess(boolean flag) {
498 if (Utils.assertionEnabled()) {
499 if (flag) {
500 pgAccessCount++;
501 allowPGAccess = true;
502 }
503 else {
504 if (pgAccessCount <= 0) {
505 throw new java.lang.AssertionError("*** pgAccessCount underflow");
506 }
591 }
592 }
593
594 /**
595 * The peer of this scene
596 */
597 private TKScene peer;
598
599 /*
600 * Get Scene's peer
601 */
602 TKScene getPeer() {
603 return peer;
604 }
605
606 /**
607 * The scene pulse listener that gets called on toolkit pulses
608 */
609 ScenePulseListener scenePulseListener = new ScenePulseListener();
610
611 TKPulseListener getScenePulseListener() {
612 if (SystemProperties.isDebug()) {
613 return scenePulseListener;
614 }
615 return null;
616 }
617
618 private List<Runnable> preLayoutPulseListeners;
619 private List<Runnable> postLayoutPulseListeners;
620
621 /**
622 * Adds a new scene pre layout pulse listener to this scene. Every time a pulse occurs,
623 * this listener will be called on the JavaFX Application Thread directly
624 * <strong>before</strong> the CSS and layout passes, and also before
625 * any rendering is done for
626 * this frame. This scene pulse listener is suitable for knowing when a
627 * scenegraph pulse is happening and also for modifying the scenegraph
628 * (as it is called before CSS and layout, so any changes made will be properly
629 * styled and positioned).
630 *
631 * This method must be called on the JavaFX Application thread.
632 *
633 * @param r The Runnable to be called when the pulse occurs.
634 *
635 * @throws IllegalStateException if this method is called on a thread
636 * other than the JavaFX Application Thread.
637 *
1266 void setNeedsRepaint() {
1267 if (this.peer != null) {
1268 peer.entireSceneNeedsRepaint();
1269 }
1270 }
1271
1272 // Process CSS and layout and sync the scene prior to the snapshot
1273 // operation of the given node for this scene (currently the node
1274 // is unused but could possibly be used in the future to optimize this)
1275 void doCSSLayoutSyncForSnapshot(Node node) {
1276 if (!sizeInitialized) {
1277 preferredSize();
1278 } else {
1279 doCSSPass();
1280 }
1281
1282 // we do not need pulse in the snapshot code
1283 // because this scene can be stage-less
1284 doLayoutPass();
1285
1286 if (!paused) {
1287 getRoot().updateBounds();
1288 if (peer != null) {
1289 peer.waitForRenderingToComplete();
1290 peer.waitForSynchronization();
1291 try {
1292 // Run the synchronizer while holding the render lock
1293 scenePulseListener.synchronizeSceneNodes();
1294 } finally {
1295 peer.releaseSynchronization(false);
1296 }
1297 } else {
1298 scenePulseListener.synchronizeSceneNodes();
1299 }
1300 }
1301
1302 }
1303
1304 // Shared method for Scene.snapshot and Node.snapshot. It is static because
1305 // we might be doing a Node snapshot with a null scene
1306 static WritableImage doSnapshot(Scene scene,
1307 double x, double y, double w, double h,
1308 Node root, BaseTransform transform, boolean depthBuffer,
1309 Paint fill, Camera camera, WritableImage wimg) {
1310
1311 Toolkit tk = Toolkit.getToolkit();
1312 Toolkit.ImageRenderingContext context = new Toolkit.ImageRenderingContext();
1313
1314 int xMin = (int)Math.floor(x);
1315 int yMin = (int)Math.floor(y);
1316 int xMax = (int)Math.ceil(x + w);
1317 int yMax = (int)Math.ceil(y + h);
1318 int width = Math.max(xMax - xMin, 1);
1319 int height = Math.max(yMax - yMin, 1);
1320 if (wimg == null) {
1463 * explicitly by the application or implicitly (such as chart animation),
1464 * the snapshot will be rendered based on the state of the scene graph at
1465 * the moment the snapshot is taken and will not reflect any subsequent
1466 * animation changes.
1467 * </p>
1468 *
1469 * @param image the writable image that will be used to hold the rendered scene.
1470 * It may be null in which case a new WritableImage will be constructed.
1471 * If the image is non-null, the scene will be rendered into the
1472 * existing image.
1473 * In this case, the width and height of the image determine the area
1474 * that is rendered instead of the width and height of the scene.
1475 *
1476 * @throws IllegalStateException if this method is called on a thread
1477 * other than the JavaFX Application Thread.
1478 *
1479 * @return the rendered image
1480 * @since JavaFX 2.2
1481 */
1482 public WritableImage snapshot(WritableImage image) {
1483 if (!paused) {
1484 Toolkit.getToolkit().checkFxUserThread();
1485 }
1486
1487 return doSnapshot(image);
1488 }
1489
1490 /**
1491 * Takes a snapshot of this scene at the next frame and calls the
1492 * specified callback method when the image is ready.
1493 * CSS and layout processing will be done for the scene prior to
1494 * rendering it.
1495 * The entire destination image is cleared using the fill {@code Paint}
1496 * of this scene. The nodes in the scene are then rendered to the image.
1497 * The point (0,0) in scene coordinates is mapped to (0,0) in the image.
1498 * If the image is smaller than the size of the scene, then the rendering
1499 * will be clipped by the image.
1500 *
1501 * <p>
1502 * This is an asynchronous call, which means that other
1503 * events or animation might be processed before the scene is rendered.
1504 * If any such events modify a node in the scene that modification will
1505 * be reflected in the rendered image (as it will also be reflected in
|
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.scene;
27
28 import com.sun.glass.ui.Application;
29 import com.sun.glass.ui.Accessible;
30 import com.sun.javafx.util.Logging;
31 import com.sun.javafx.util.Utils;
32 import com.sun.javafx.application.PlatformImpl;
33 import com.sun.javafx.collections.TrackableObservableList;
34 import com.sun.javafx.css.StyleManager;
35 import com.sun.javafx.cursor.CursorFrame;
36 import com.sun.javafx.event.EventQueue;
37 import com.sun.javafx.geom.PickRay;
38 import com.sun.javafx.geom.Vec3d;
39 import com.sun.javafx.geom.transform.BaseTransform;
40 import com.sun.javafx.perf.PerformanceTracker;
41 import com.sun.javafx.scene.CssFlags;
42 import com.sun.javafx.scene.LayoutFlags;
43 import com.sun.javafx.scene.SceneEventDispatcher;
44 import com.sun.javafx.scene.SceneHelper;
45 import com.sun.javafx.scene.input.DragboardHelper;
46 import com.sun.javafx.scene.input.ExtendedInputMethodRequests;
47 import com.sun.javafx.scene.input.InputEventUtils;
48 import com.sun.javafx.scene.input.PickResultChooser;
49 import com.sun.javafx.scene.traversal.Direction;
50 import com.sun.javafx.scene.traversal.SceneTraversalEngine;
51 import com.sun.javafx.scene.traversal.TopMostTraversalEngine;
52 import com.sun.javafx.sg.prism.NGCamera;
53 import com.sun.javafx.sg.prism.NGLightBase;
54 import com.sun.javafx.tk.*;
55 import com.sun.prism.impl.PrismSettings;
56
57 import javafx.animation.KeyFrame;
58 import javafx.animation.Timeline;
59 import javafx.application.ConditionalFeature;
60 import javafx.application.Platform;
392 public void preferredSize(Scene scene) {
393 scene.preferredSize();
394 }
395
396 @Override
397 public void disposePeer(Scene scene) {
398 scene.disposePeer();
399 }
400
401 @Override
402 public void initPeer(Scene scene) {
403 scene.initPeer();
404 }
405
406 @Override
407 public void setWindow(Scene scene, Window window) {
408 scene.setWindow(window);
409 }
410
411 @Override
412 public TKScene getPeer(Scene scene) {
413 return scene.getPeer();
414 }
415
416 @Override
417 public void setAllowPGAccess(boolean flag) {
418 Scene.setAllowPGAccess(flag);
419 }
420
421 @Override
422 public void parentEffectiveOrientationInvalidated(
423 final Scene scene) {
424 scene.parentEffectiveOrientationInvalidated();
425 }
426
427 @Override
428 public Camera getEffectiveCamera(Scene scene) {
429 return scene.getEffectiveCamera();
430 }
431
432 @Override
433 public Scene createPopupScene(Parent root) {
434 return new Scene(root) {
435 @Override
436 void doLayoutPass() {
437 resizeRootToPreferredSize(getRoot());
438 super.doLayoutPass();
439 }
440
441 @Override
453 scene.transientFocusContainer = node;
454 }
455 }
456
457 @Override
458 public Accessible getAccessible(Scene scene) {
459 return scene.getAccessible();
460 }
461 });
462 }
463
464 // Reserve space for 30 nodes in the dirtyNodes set.
465 private static final int MIN_DIRTY_CAPACITY = 30;
466
467 // For debugging
468 private static boolean inSynchronizer = false;
469 private static boolean inMousePick = false;
470 private static boolean allowPGAccess = false;
471 private static int pgAccessCount = 0;
472
473 /**
474 * Used for debugging purposes. Returns true if we are in either the
475 * mouse event code (picking) or the synchronizer, or if the scene is
476 * not yet initialized,
477 *
478 */
479 static boolean isPGAccessAllowed() {
480 return inSynchronizer || inMousePick || allowPGAccess;
481 }
482
483 static void setAllowPGAccess(boolean flag) {
484 if (Utils.assertionEnabled()) {
485 if (flag) {
486 pgAccessCount++;
487 allowPGAccess = true;
488 }
489 else {
490 if (pgAccessCount <= 0) {
491 throw new java.lang.AssertionError("*** pgAccessCount underflow");
492 }
577 }
578 }
579
580 /**
581 * The peer of this scene
582 */
583 private TKScene peer;
584
585 /*
586 * Get Scene's peer
587 */
588 TKScene getPeer() {
589 return peer;
590 }
591
592 /**
593 * The scene pulse listener that gets called on toolkit pulses
594 */
595 ScenePulseListener scenePulseListener = new ScenePulseListener();
596
597 private List<Runnable> preLayoutPulseListeners;
598 private List<Runnable> postLayoutPulseListeners;
599
600 /**
601 * Adds a new scene pre layout pulse listener to this scene. Every time a pulse occurs,
602 * this listener will be called on the JavaFX Application Thread directly
603 * <strong>before</strong> the CSS and layout passes, and also before
604 * any rendering is done for
605 * this frame. This scene pulse listener is suitable for knowing when a
606 * scenegraph pulse is happening and also for modifying the scenegraph
607 * (as it is called before CSS and layout, so any changes made will be properly
608 * styled and positioned).
609 *
610 * This method must be called on the JavaFX Application thread.
611 *
612 * @param r The Runnable to be called when the pulse occurs.
613 *
614 * @throws IllegalStateException if this method is called on a thread
615 * other than the JavaFX Application Thread.
616 *
1245 void setNeedsRepaint() {
1246 if (this.peer != null) {
1247 peer.entireSceneNeedsRepaint();
1248 }
1249 }
1250
1251 // Process CSS and layout and sync the scene prior to the snapshot
1252 // operation of the given node for this scene (currently the node
1253 // is unused but could possibly be used in the future to optimize this)
1254 void doCSSLayoutSyncForSnapshot(Node node) {
1255 if (!sizeInitialized) {
1256 preferredSize();
1257 } else {
1258 doCSSPass();
1259 }
1260
1261 // we do not need pulse in the snapshot code
1262 // because this scene can be stage-less
1263 doLayoutPass();
1264
1265 getRoot().updateBounds();
1266 if (peer != null) {
1267 peer.waitForRenderingToComplete();
1268 peer.waitForSynchronization();
1269 try {
1270 // Run the synchronizer while holding the render lock
1271 scenePulseListener.synchronizeSceneNodes();
1272 } finally {
1273 peer.releaseSynchronization(false);
1274 }
1275 } else {
1276 scenePulseListener.synchronizeSceneNodes();
1277 }
1278
1279 }
1280
1281 // Shared method for Scene.snapshot and Node.snapshot. It is static because
1282 // we might be doing a Node snapshot with a null scene
1283 static WritableImage doSnapshot(Scene scene,
1284 double x, double y, double w, double h,
1285 Node root, BaseTransform transform, boolean depthBuffer,
1286 Paint fill, Camera camera, WritableImage wimg) {
1287
1288 Toolkit tk = Toolkit.getToolkit();
1289 Toolkit.ImageRenderingContext context = new Toolkit.ImageRenderingContext();
1290
1291 int xMin = (int)Math.floor(x);
1292 int yMin = (int)Math.floor(y);
1293 int xMax = (int)Math.ceil(x + w);
1294 int yMax = (int)Math.ceil(y + h);
1295 int width = Math.max(xMax - xMin, 1);
1296 int height = Math.max(yMax - yMin, 1);
1297 if (wimg == null) {
1440 * explicitly by the application or implicitly (such as chart animation),
1441 * the snapshot will be rendered based on the state of the scene graph at
1442 * the moment the snapshot is taken and will not reflect any subsequent
1443 * animation changes.
1444 * </p>
1445 *
1446 * @param image the writable image that will be used to hold the rendered scene.
1447 * It may be null in which case a new WritableImage will be constructed.
1448 * If the image is non-null, the scene will be rendered into the
1449 * existing image.
1450 * In this case, the width and height of the image determine the area
1451 * that is rendered instead of the width and height of the scene.
1452 *
1453 * @throws IllegalStateException if this method is called on a thread
1454 * other than the JavaFX Application Thread.
1455 *
1456 * @return the rendered image
1457 * @since JavaFX 2.2
1458 */
1459 public WritableImage snapshot(WritableImage image) {
1460 Toolkit.getToolkit().checkFxUserThread();
1461
1462 return doSnapshot(image);
1463 }
1464
1465 /**
1466 * Takes a snapshot of this scene at the next frame and calls the
1467 * specified callback method when the image is ready.
1468 * CSS and layout processing will be done for the scene prior to
1469 * rendering it.
1470 * The entire destination image is cleared using the fill {@code Paint}
1471 * of this scene. The nodes in the scene are then rendered to the image.
1472 * The point (0,0) in scene coordinates is mapped to (0,0) in the image.
1473 * If the image is smaller than the size of the scene, then the rendering
1474 * will be clipped by the image.
1475 *
1476 * <p>
1477 * This is an asynchronous call, which means that other
1478 * events or animation might be processed before the scene is rendered.
1479 * If any such events modify a node in the scene that modification will
1480 * be reflected in the rendered image (as it will also be reflected in
|