1 2 Previous Next 17 Replies Latest reply: Dec 3, 2011 10:23 AM by swpalmer RSS

    Image conversion between AWT and FX

    DarrylBurke
      It isn't difficult to write a utility method to convert a non-animated <tt>java.awt.Image</tt> to <tt>javafx.scene.image.Image</tt>. But short of using <tt>Robot</tt> to obtain a screenshot (and hope that no part of the fx Image is obscured) is there a way to do the reverse? And is there a way to convert an animated <tt>java.awt.Image</tt> to <tt>javafx.scene.image.Image</tt>?
        public static javafx.scene.image.Image createImage(java.awt.Image image) throws IOException {
          if (!(image instanceof RenderedImage)) {
            BufferedImage bufferedImage = new BufferedImage(image.getWidth(null),
                    image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
            Graphics g = bufferedImage.createGraphics();
            g.drawImage(image, 0, 0, null);
            g.dispose();
      
            image = bufferedImage;
          }
          ByteArrayOutputStream out = new ByteArrayOutputStream();
          ImageIO.write((RenderedImage) image, "png", out);
          out.flush();
          ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
          return new javafx.scene.image.Image(in);
        }
      Any inputs are appreciated.

      db
        • 1. Re: Image conversion between AWT and FX
          aidreamer
          There is no way short of using hacks, but maybe you could extend JFXPanel and call its http://download.oracle.com/javafx/2.0/api/javafx/embed/swing/JFXPanel.html#paintComponent%28java.awt.Graphics%29 method to draw to your Image's Graphics object. I can't try this out myself though, so I don't know if it'll work.
          • 2. Re: Image conversion between AWT and FX
            DarrylBurke
            Thanks aidreamer. That's a good suggestion, but so far I've only been able to paint the <tt>JFXPanel</tt> to a <tt>BufferedImage</tt> when the panel is actiually showing on screen. The old Swing hack of overriding <tt>isShowing()</tt> to always return true didn't help, nor did realizing the panel by putting it in a <tt>JFrame</tt> and calling <tt>pack()</tt> (without <tt>setVisible(true)</tt>).

            I suspect some asynchronicity between the Swing EDT and the FX UI thread, but so far my attempts to attain a correct sequence of events just ended up with a horribly complicated load of <tt>Runnable</tt>s and no image. I have to be away from my computer for a few hours, but shall get back to this later and post whatever code I have for further suggestions.

            Thanks again, db
            • 3. Re: Image conversion between AWT and FX
              jojorabbit
              Hi,
              i have similar problem, but i want to generate n images(lets say 5). I have tried a lot of things the only one that worked was JDialog instead of JFrame and JFXPane and showing dialog 5 times to generate each image.
              Showing Jframe 5 times makes 5 frames even if i dispose them/manually/hide them/anything there are still 5 frames that is why i used JDialog.
              If i try to change content of scene (scene that is in JFXPanel) images are generated as half of image, blank white, transparent etc not like expected.
              I have tried even to set dialog visible true then generate then visible to false, Jdialog shows and it disappears fast but still images are blank.
              If JFXPanel is not shown i always get blank transparent image or half of image, or something wierd.

              So my suggestion is that we all post what we have and see what can be done. I just need to clean up my code and will post it as soon as possible.

              Thanks for any help.
              • 4. Re: Image conversion between AWT and FX
                DarrylBurke
                edit: Everything's static because I planned this as a pair of utility methods, however I ended up with a boolean field that isn't thread-safe. That definitely needs to be changed.
                -----
                Here you go. Please criticize freely, I'm not happy with waiting 2 seconds (which may be overkill) but at least this gives results without making the JFrame visible. As far as I can analyze, the two <tt>createImage</tt> methods should be safe to call from any thread except the EDT.
                import java.awt.Graphics;
                import java.awt.event.ActionEvent;
                import java.awt.event.ActionListener;
                import java.awt.image.BufferedImage;
                import java.awt.image.RenderedImage;
                import java.io.ByteArrayInputStream;
                import java.io.ByteArrayOutputStream;
                import java.io.File;
                import java.io.IOException;
                import javafx.application.Application;
                import javafx.application.Platform;
                import javafx.embed.swing.JFXPanel;
                import javafx.scene.Scene;
                import javafx.scene.image.ImageView;
                import javafx.scene.layout.BorderPane;
                import javafx.stage.Stage;
                import javax.imageio.ImageIO;
                import javax.swing.*;
                
                public class ImageConverter extends Application {
                
                  private static boolean imageReady;
                
                  public static void main(String[] args) throws Exception {
                    Application.launch(ImageConverter.class, args);
                    testAwtToFx();
                    testFxToAwt();
                  }
                
                  @Override
                  public void start(Stage primaryStage) throws Exception {
                  }
                
                  private static void testAwtToFx() throws Exception {
                    BufferedImage awtImage = ImageIO.read(new File("E:/path/to/image/imagefile.jpg"));
                    final javafx.scene.image.Image fxImage = createImage(awtImage);
                    Platform.runLater(new Runnable() {
                
                      @Override
                      public void run() {
                        ImageView imageView = new ImageView(fxImage);
                        BorderPane borderPane = new BorderPane();
                        borderPane.setCenter(imageView);
                        Scene scene = new Scene(borderPane, fxImage.getWidth(), fxImage.getHeight());
                        Stage stage = new Stage();
                        stage.setScene(scene);
                        stage.setVisible(true);
                      }
                    });
                  }
                
                  private static void testFxToAwt() {
                    javafx.scene.image.Image fxImage = new javafx.scene.image.Image("E:/path/to/image/imagefile.jpg");
                    final java.awt.Image awtImage = createImage(fxImage);
                    SwingUtilities.invokeLater(new Runnable() {
                
                      @Override
                      public void run() {
                        JOptionPane.showMessageDialog(null, new ImageIcon(awtImage));
                      }
                    });
                
                  }
                
                  public static javafx.scene.image.Image createImage(java.awt.Image image) throws IOException {
                    if (!(image instanceof RenderedImage)) {
                      BufferedImage bufferedImage = new BufferedImage(image.getWidth(null),
                              image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
                      Graphics g = bufferedImage.createGraphics();
                      g.drawImage(image, 0, 0, null);
                      g.dispose();
                      image = bufferedImage;
                    }
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    ImageIO.write((RenderedImage) image, "png", out);
                    out.flush();
                    ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
                    return new javafx.scene.image.Image(in);
                  }
                
                  public static java.awt.Image createImage(final javafx.scene.image.Image image) {
                    final int DELAY = 2000;
                    final int width = (int) image.getWidth();
                    final int height = (int) image.getHeight();
                    final BufferedImage awtImage = new BufferedImage(width, height,
                            BufferedImage.TYPE_INT_ARGB);
                    final Graphics g = awtImage.createGraphics();
                    imageReady = false;
                    Platform.runLater(new Runnable() {
                
                      @Override
                      public void run() {
                        ImageView imageView = new ImageView(image);
                        BorderPane borderPane = new BorderPane();
                        borderPane.setCenter(imageView);
                        Scene scene = new Scene(borderPane, width, height);
                        final JFXPanel panel = new JFXPanel();
                        panel.setScene(scene);
                        SwingUtilities.invokeLater(new Runnable() {
                
                          @Override
                          public void run() {
                            panel.setSize(width, height);
                            Timer timer = new Timer(DELAY, new ActionListener() {
                
                              @Override
                              public void actionPerformed(ActionEvent e) {
                                panel.paint(g);
                                g.dispose();
                                imageReady = true;
                              }
                            });
                            timer.setRepeats(false);
                            timer.start();
                          }
                        });
                      }
                    });
                    while (!imageReady) {
                      try {
                        Thread.sleep(DELAY);
                      } catch (InterruptedException ex) {
                        ex.printStackTrace();
                      }
                    }
                    return awtImage;
                  }
                }
                When you run it, you'll see the Stage appear, then after a couple of seconds the JOptionPane will turn up.

                db

                edit2: Shifted some stuff around and got rid of the JFrame entirely.

                Edited by: Darryl Burke
                • 5. Re: Image conversion between AWT and FX
                  jojorabbit
                  Hi,
                  nice code, will criticize it later just need to test it a little bit an clean up my code, post my code, finish some work etc, code will come soon.

                  Q1. I noticed that when i close stage program is still running is that supposed to happen or just bug in code i guess? Output image from FX to file is never saved on HDD or i did not notice it.

                  I like that 2 second pause but it makes problem for me because i want as i said n images n goes from 1 to 60 max and 60x2sec is 2 minutes of waiting and that is bad. Will try to see how my code will work with pausing
                  I will post code and critics as edit to this post to avoid spamming. Will put numbers in front of my questions critics and so on, so when you answer please use same number as the question so that it is easier to follow up.

                  EDITED:
                  Well here is my code finally. Everything remains same as Darryl said feel free to criticize, ask questions etc etc.
                  About those 2 seconds of waiting i used 200ms it works even with 5ms did not try below 5, feel free to try it. I have tried even with timer but timeline seems to be better just need to try AnimationTimer.
                  And yes i have some Runnables too, i don't have time to try to run project with profiler so if anyone tries please post results so we all know what we have.
                  As i said up there when i close stage in your app process still remains, here i was paying attention to close/dispose everything so when u close main stage there is no process that still runs.
                  I found bug in old code that made that 5 frames to show up, now it works even with JFrame.
                  I remember that long time ago i had to hide JFrame and to keep process running so if we can "hide" JFrame and keep process running am not sure that it will generate images as expected if someone tries it post results.
                  I tried to minimize it and lower the delay to 5 it is pretty fast even sometimes not so easy to notice but what if we need to generate 10 images 1024x1024 that won't be so fast.

                  Here is a little bit about code i used rectangles as nodes not ImageViews, ImageView and Rectangle both inherit from Node. And with images in code everyone who copies it needs to change urls and similar stuff, so code should work with any node (Rectangle, ImageView, Button...).
                  It has a lot of comments, some are just to remind me what i need some are just to comment some lines, some are just TODO, some are warnings ... i like to have nice code just my habit. This is not finished as i wanted but i don't have too much time now.
                  In code are 2 "package protected" classes NodeThumb and implementations of generate handler.
                  Node thumb has "custom" pseudoclass selected so it may help to write new pseudo classes for customNodes and so on. I needed selected in my program so i did not want to change 2 much in this code.
                  Utils class contains utility methods for generation based on this blog post: http://blogs.oracle.com/rakeshmenonp/entry/javafx_save_as_image

                  test.css
                  .thumb *.container{
                      -fx-opacity: 0.65;
                      -fx-border-color: black;
                      -fx-border-width: 2.5;
                      -fx-border-radius: 5;
                      -fx-border-style: dashed;
                      -fx-border-insets: 5;
                  }
                  
                  .thumb:hover *.container{
                      -fx-opacity: 1.0;
                      -fx-border-color: black;
                      -fx-border-width: 2.5;
                      -fx-border-radius: 5;
                      -fx-border-style: solid;
                      -fx-border-insets: 5;
                  }
                  
                  .thumb:selected *.container {
                      -fx-opacity: 1.0;
                      -fx-border-color: red;
                      -fx-border-width: 2.5;
                      -fx-border-radius: 5;
                      -fx-border-insets: 5;
                      -fx-border-style: solid;
                  }
                  Utils.java
                  package playground;
                  
                  import javafx.scene.Node;
                  import java.awt.Container;
                  import java.awt.Frame;
                  import java.awt.Graphics;
                  import java.awt.image.BufferedImage;
                  import javax.swing.JFrame;
                  import java.io.File;
                  import javafx.geometry.Bounds;
                  import javax.imageio.ImageIO;
                  
                  /**
                   * 
                   * Utility methods.
                   * Am not the author of this class. I just added/changed some lines that i needed.
                   * It was inspired by this blogpost: http://blogs.oracle.com/rakeshmenonp/entry/javafx_save_as_image
                   */
                  public class Utils {
                  
                      public static Container getContainer() {
                          Frame[] frames = Frame.getFrames();
                          System.out.println("frames no: " + frames.length); // when using JFrame Frame.getFrames returns more then 1 frame sometimes if u make bug like mine
                          return ((JFrame) frames[0]).getContentPane();
                      }
                  
                      public static void saveNode(Node node, File file) {
                          if (file == null) {
                              return;
                          }
                          save(getContainer(), node.localToScene(node.getBoundsInLocal()), file);
                      }
                  
                      public static void save(Container container, Bounds bounds, File file) {
                          try {
                              ImageIO.write(toBufferedImage(container, bounds), "png", file);
                              System.out.println("Image saved to " + file.getAbsolutePath());
                          } catch (java.lang.Exception ignored) {
                          }
                      }
                  
                      public static BufferedImage toBufferedImage(Container container, Bounds bounds) {
                          BufferedImage bufferedImage = new BufferedImage(
                                  (int) bounds.getWidth(),
                                  (int) bounds.getHeight(),
                                  BufferedImage.TYPE_INT_ARGB);
                  
                          Graphics graphics = bufferedImage.getGraphics();
                          graphics.translate((int) -bounds.getMinX(), (int) -bounds.getMinY()); // translating to upper-left corner
                          container.paint(graphics);
                          graphics.dispose();
                          return bufferedImage;
                      }
                  }
                  And finally the longest one
                  NodeToImageTest.java
                  package playground;
                  
                  import java.awt.Frame;
                  import java.awt.event.WindowAdapter;
                  import java.io.File;
                  import java.util.List;
                  import java.util.Random;
                  import javafx.animation.KeyFrame;
                  import javafx.animation.Timeline;
                  import javafx.application.Application;
                  import javafx.application.Platform;
                  import javafx.beans.property.BooleanProperty;
                  import javafx.beans.property.ObjectProperty;
                  import javafx.beans.value.ChangeListener;
                  import javafx.beans.value.ObservableValue;
                  import javafx.collections.FXCollections;
                  import javafx.collections.ObservableList;
                  import javafx.embed.swing.JFXPanel;
                  import javafx.event.ActionEvent;
                  import javafx.event.EventHandler;
                  import javafx.geometry.Orientation;
                  import javafx.scene.Group;
                  import javafx.scene.Parent;
                  import javafx.scene.Scene;
                  import javafx.scene.control.Button;
                  import javafx.scene.control.SplitPane;
                  import javafx.scene.input.MouseEvent;
                  import javafx.scene.layout.HBox;
                  import javafx.scene.layout.StackPane;
                  import javafx.scene.paint.Color;
                  import javafx.scene.shape.Rectangle;
                  import javafx.stage.Stage;
                  import javafx.util.Duration;
                  import javax.swing.JFrame;
                  import javax.swing.SwingUtilities;
                  
                  
                  public class NodeToImageTest extends Application {
                  
                      private Scene scene;
                      private Group root;
                      private StackPane mainView;
                      private GenerateEventHandler geh;
                      private int itemNo = 9; // number of items, change and play
                      private NodeThumb lastThumb = null; // to memorize last thumb so it can be easly deselected
                      private ObjectProperty<Rectangle> selectedItem = new ObjectProperty<Rectangle>();
                      private ObservableList<Rectangle> items = FXCollections.<Rectangle>observableArrayList();
                      private ObservableList<NodeThumb> thumbs = FXCollections.<NodeThumb>observableArrayList();
                  
                      /**
                       * Main method
                       * @param args 
                       */
                      public static void main(String[] args) {
                          Application.launch(NodeToImageTest.class, args);
                      }
                  
                      @Override
                      public void start(Stage stage) throws Exception {
                          stage.setTitle("Generating images.");
                          createItems();
                          // usual stuff
                          root = new Group();
                          scene = new Scene(root, 800, 700);
                          setupCSS();
                          stage.setScene(scene);
                          mainView = new StackPane();
                  
                          //
                          SplitPane sp = new SplitPane();
                          sp.setOrientation(Orientation.VERTICAL);
                          sp.setDividerPositions(0.70d);
                          sp.setPrefSize(800, 700);
                          //
                          // box for thumbs
                          HBox thumbHBox = new HBox(10);
                          thumbs = createThumbs();
                          thumbHBox.getChildren().addAll(thumbs);
                  
                          // listener for selectedItem
                          selectedItem.addListener(new ChangeListener<Rectangle>() {
                  
                              @Override
                              public void changed(ObservableValue<? extends Rectangle> observable, Rectangle oldValue, Rectangle newValue) {
                                  System.out.println("selected item : " + newValue);           
                                  selectedItem.set(newValue); // later add fadeOut/fadeIn between changing items
                                  mainView.getChildren().setAll(newValue);
                                  // <comment-uncomment>                
                                  mainView.impl_transformsChanged(); // manual refreresh BUG : RT-13860
                                  // </comment-uncomment>
                              }
                          });
                  
                          // box of controls
                          HBox controlsHBox = new HBox(10);
                          controlsHBox.setStyle("-fx-padding: 10 10 10 10;");
                          Button generateImages = new Button("Generate");
                          geh = new GenerateEventHandler();
                          generateImages.setOnAction(geh);
                          controlsHBox.getChildren().add(generateImages);
                          //
                          // usual stuff
                          sp.getItems().add(mainView);
                          sp.getItems().add(thumbHBox);
                          sp.getItems().add(controlsHBox);
                          //
                          root.getChildren().add(sp);
                          lastThumb = thumbs.get(0);
                          lastThumb.setSelected(true);
                          selectedItem.set(lastThumb.rect);
                          stage.centerOnScreen();
                          stage.setVisible(true);
                      }
                  
                      /**
                       * Method for creating list of Thumbs
                       * @return list of thumbs
                       */
                      private ObservableList<NodeThumb> createThumbs() {
                          ObservableList<NodeThumb> localList = FXCollections.observableArrayList();
                          for (Rectangle item : items) {
                              NodeThumb thumb = new NodeThumb(item);
                              thumb.setId(item.getId() + "-thumb");
                              thumb.setOnMouseClicked(new EventHandler<MouseEvent>() {
                  
                                  @Override
                                  public void handle(MouseEvent event) {
                                      // this handler impl can be done in better way
                                      System.out.println("src: " + event.getSource());
                                      NodeThumb clickedThumb = (NodeThumb) event.getSource();
                                      if (lastThumb == clickedThumb) {
                                          return;
                                      }
                                      if (lastThumb != null) {
                                          lastThumb.setSelected(false);
                                      }
                                      selectedItem.set(clickedThumb.rect);
                                      clickedThumb.setSelected(true);
                                      lastThumb = clickedThumb;
                                      System.out.println("mainView content -> " + mainView.getChildren());
                                  }
                              });
                              localList.add(thumb);
                          }
                          return localList;
                      }
                  
                      /**
                       * setup css
                       */
                      protected void setupCSS() {
                          scene.getStylesheets().add("/playground/test.css");
                      }
                  
                      /**
                       * Utility method to crate random rectangles
                       */
                      private void createItems() {
                          Random colorRandomizer = new Random();
                          for (int i = 0; i < itemNo; i++) {
                              Rectangle temp = new Rectangle(300, 300,
                                      Color.color(
                                      colorRandomizer.nextDouble(),
                                      colorRandomizer.nextDouble(),
                                      colorRandomizer.nextDouble()));
                              temp.setId("item_" + (i + 1));
                              items.add(temp);
                          }
                      }
                  // *********************************
                  //   <protected class declaration> - handler
                  // *********************************
                  
                      class GenerateEventHandler implements EventHandler<ActionEvent> {
                  
                          // todo add some progressbar for waiting
                          JFrame frame; // don't make frame = new Frame in handler with generating n images because Frame.getFrames returns more then 1
                          Scene handlerScene;
                          Group handlerRoot;
                          JFXPanel panel;
                          Button handlerGenerate;
                          StackPane stackPane;
                          
                          int currentItem = 0; // current item to generate
                          public static final int DELAY = 200; // lower the delay for faster generation
                          Timeline timer; // try to use AnimationTimer
                          Stage localStage; // try to make in front of dialog to hide dialog behind it
                  
                          public GenerateEventHandler() {
                              // <DOES NOT THROW NOT IN FXAppThread> -> so i guess this is ok, if it is not ok feel free to post your comment about it and give some arguments
                              frame = new JFrame();
                              frame.setSize(500, 500);
                  //            frame.setState(JFrame.ICONIFIED); // a little trick -> minimize jFrame -> TODO: add Text or something to mainView.children to indicate generating
                  //            frame.setUndecorated(true); // looks ugly -> how to change background of JFXFrame to transparent?  
                              frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
                              frame.setLocationRelativeTo(null);
                              frame.setResizable(false);
                              frame.setAlwaysOnTop(true);
                              // </DOES NOT THROW NOT in FXAppThread>             
                              //         
                              // timer
                              timer = new Timeline();
                              timer.getKeyFrames().addAll(new KeyFrame(Duration.valueOf(DELAY), new EventHandler<ActionEvent>() { // keyframe 2
                  
                                  @Override
                                  public void handle(ActionEvent event) {
                                      stackPane.getChildren().setAll(items.get(currentItem));
                                      stackPane.impl_transformsChanged();
                                      System.out.println("TIMER HANDLER.");
                                      // TODO: add later some stage/dialog with progress 
                                      handlerGenerate.getOnAction().handle(new ActionEvent("timer", null)); // "timer", just to know that timer called handle
                  
                                  }
                              }));
                              timer.setCycleCount(1);
                          }
                  
                          @Override
                          public void handle(ActionEvent event) {
                              // Handler source is always timer except first time when generateButton is pressed
                              System.out.println("-> HANDLER SOURCE <- " + event.getSource());
                              if (currentItem == items.size()) {
                                  currentItem = 0;
                                  //
                                  stackPane.getChildren().clear();
                                  frame.setVisible(false);
                                  frame.dispose();
                                  // get back to main window
                                  selectedItem.set(lastThumb.rect); 
                                  return;
                              }
                              mainView.getChildren().clear();
                  
                  
                              // check no of frames
                              Frame[] frames = Frame.getFrames();
                              System.out.println("frames count: " + frames.length);
                              // 
                              // init components
                  
                              handlerGenerate = new Button("Am hidden...");
                              handlerGenerate.setVisible(false); // hide button
                              handlerGenerate.setOnAction(new EventHandler<ActionEvent>() {
                  
                                  @Override
                                  public void handle(ActionEvent event) {
                                      // generate all images
                                      Utils.save(panel, stackPane.localToScene(stackPane.getBoundsInLocal()), new File("item-" + (currentItem + 1) + ".png")); // save to images as item-1.png etc
                  //                    Utils.saveNode(stackPane, new File("item-" + (currentItem + 1) + ".png")); // this works 2 since there is only 1 frame
                                      currentItem++; // move to next item                    
                                      geh.handle(event); // call handler -> bring dialog with new content -> simulating new click on hiddenButton
                                  }
                              });
                              // usual stuff
                              stackPane = new StackPane();
                              stackPane.getChildren().setAll(items.get(currentItem));
                              stackPane.setTranslateX(50);
                              stackPane.setTranslateY(50);
                              handlerRoot = new Group();
                              handlerRoot.getChildren().addAll(handlerGenerate, stackPane);
                  
                              handlerScene = new Scene(handlerRoot);
                              panel = new JFXPanel();
                              panel.setScene(handlerScene);
                              
                  
                              // <INVOKE LATER>
                              SwingUtilities.invokeLater(new Runnable() {
                  
                                  @Override
                                  public void run() {
                                      System.out.println("-> IN RUNNABLE <- isFXAppThread: " + Platform.isFxApplicationThread());
                                      System.out.println("-> IN RUNNABLE <- isEDT: " + SwingUtilities.isEventDispatchThread());
                                      frame.setContentPane(panel);
                  
                                      // <THIS IS JUST TIP/WARNING>
                                      // *********** DO NOT DO THIS NEVER! ******************
                                      frame.addWindowListener(new WindowAdapter() {
                                          // NOTE HERE ONLY EDT JOBS NOT FX JOBS!
                                          // mainView.getChildren().clear(); // 
                                          // THIS THROWS NOT ON FXAppThread                     
                                      }); // THIS IS NOT FX APP THREAD!
                                      // *********** DO NOT DO THIS NEVER! ******************
                                      // </THIS IS JUST TIP/WARNING>
                  
                                      frame.setAlwaysOnTop(true);
                                      frame.setVisible(true);
                  
                                      // <RUN LATER>
                                      Platform.runLater(new Runnable() {
                  
                                          @Override
                                          public void run() {
                                              timer.playFromStart(); // starts timer -> calls handlerGenerate onAction
                                          }
                                      }); // </RUN LATER>
                                  }
                              }); // </INVOKE LATER>
                  
                          }
                      }
                  // *********************************
                  //   <\protected class declaration> - handler
                  // *********************************
                  
                  // *********************************
                  //   <protected class declaration>
                  // *********************************
                      class NodeThumb extends Parent {
                  
                          Rectangle rect;
                          private static final String PSEUDO_CLASS_SELECTED = "selected";
                          private BooleanProperty selected; // property for pseudo class.
                          private StackPane container;
                  
                          public NodeThumb(Rectangle rect) {
                              this.rect = rect;
                              getStyleClass().add("thumb");
                              initComponents();
                          }
                  
                          public BooleanProperty selectedProperty() {
                              if (selected == null) {
                                  selected = new BooleanProperty() {
                  
                                      @Override
                                      protected void store(boolean value) {
                                          super.store(value);
                                          NodeThumb.this.impl_pseudoClassStateChanged(PSEUDO_CLASS_SELECTED);
                                      }
                                  };
                              }
                              return selected;
                          }
                  
                          public boolean isSelected() {
                  
                              return this.selected == null ? false : this.selected.get();
                          }
                  
                          public final void setSelected(boolean value) {
                              if (value != isSelected()) {
                                  selectedProperty().set(value);
                              }
                          }
                  
                          @Override
                          public void impl_getPseudoClassState(List<String> list) {
                              super.impl_getPseudoClassState(list);
                              if (isSelected()) {
                                  list.add(PSEUDO_CLASS_SELECTED);
                              }
                          }
                  
                          private void initComponents() {
                              // 6 times smaller
                              Rectangle thumb = new Rectangle();
                  
                              thumb.widthProperty().bind(rect.widthProperty().divide(6.0d));
                              thumb.heightProperty().bind(rect.heightProperty().divide(6.0d));
                              thumb.fillProperty().bind(rect.fillProperty());
                              // each side 5 px
                              Rectangle bgRect = new Rectangle();
                              bgRect.widthProperty().bind(thumb.widthProperty().add(25));
                              bgRect.heightProperty().bind(thumb.heightProperty().add(25));
                              bgRect.setFill(Color.TRANSPARENT);
                              //
                              container = new StackPane();
                              container.getStyleClass().add("container");
                              container.getChildren().addAll(bgRect, thumb);
                              getChildren().add(container);
                          }
                      }
                  // **********************************
                  //   </protected class declaration>
                  // **********************************
                  }
                  It maybe seems complicated but it is not.
                  Hope you like it and hope it helps.

                  Edited by: jojorabbit on Jun 12, 2011 4:00 AM
                  • 6. Re: Image conversion between AWT and FX
                    DarrylBurke
                    Thanks jojorabbit. It's going to take me some time to read your code and figure out the flow.

                    Meanwhile, here's my latest iteration with two very similar approaches, one using a Swing Timer and the other with Timeline. Both need to be called on a Thread other than the FX Application thread, and both suffer from the problem of preventing the program from terminating. The debugger shows both the EDT and the FX Application thread running their respective event loops.

                    <tt>ImageConverter.java</tt>
                    import java.awt.Graphics;
                    import java.awt.event.ActionEvent;
                    import java.awt.event.ActionListener;
                    import java.awt.image.BufferedImage;
                    import java.awt.image.RenderedImage;
                    import java.io.ByteArrayInputStream;
                    import java.io.ByteArrayOutputStream;
                    import java.io.IOException;
                    import javafx.animation.KeyFrame;
                    import javafx.animation.Timeline;
                    import javafx.application.Platform;
                    import javafx.embed.swing.JFXPanel;
                    import javafx.event.EventHandler;
                    import javafx.scene.Scene;
                    import javafx.scene.image.ImageView;
                    import javafx.scene.layout.BorderPane;
                    import javafx.util.Duration;
                    import javax.imageio.ImageIO;
                    import javax.swing.SwingUtilities;
                    import javax.swing.Timer;
                    
                    public class ImageConverter {
                    
                      private boolean imageReady;
                    
                      public static javafx.scene.image.Image createImage(java.awt.Image image) throws IOException {
                        if (!(image instanceof RenderedImage)) {
                          BufferedImage bufferedImage = new BufferedImage(image.getWidth(null),
                                  image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
                          Graphics g = bufferedImage.createGraphics();
                          g.drawImage(image, 0, 0, null);
                          g.dispose();
                          image = bufferedImage;
                        }
                        ByteArrayOutputStream out = new ByteArrayOutputStream();
                        ImageIO.write((RenderedImage) image, "png", out);
                        out.flush();
                        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
                        return new javafx.scene.image.Image(in);
                      }
                    
                      public java.awt.Image createImage(final javafx.scene.image.Image image) {
                        final int DELAY = 20;
                        final int width = (int) image.getWidth();
                        final int height = (int) image.getHeight();
                        final BufferedImage awtImage = new BufferedImage(width, height,
                                BufferedImage.TYPE_INT_ARGB);
                        final Graphics g = awtImage.createGraphics();
                        imageReady = false;
                        Platform.runLater(new Runnable() {
                    
                          @Override
                          public void run() {
                            ImageView imageView = new ImageView(image);
                            BorderPane borderPane = new BorderPane();
                            borderPane.setCenter(imageView);
                            Scene scene = new Scene(borderPane, width, height);
                            final JFXPanel panel = new JFXPanel();
                            panel.setScene(scene);
                            SwingUtilities.invokeLater(new Runnable() {
                    
                              @Override
                              public void run() {
                                panel.setSize(width, height);
                                Timer timer = new Timer(DELAY, new ActionListener() {
                    
                                  @Override
                                  public void actionPerformed(ActionEvent e) {
                                    panel.paint(g);
                                    g.dispose();
                                    imageReady = true;
                                  }
                                });
                                timer.setRepeats(false);
                                timer.start();
                              }
                            });
                          }
                        });
                        while (!imageReady) {
                          try {
                            Thread.sleep(DELAY);
                          } catch (InterruptedException ex) {
                            ex.printStackTrace();
                          }
                        }
                        return awtImage;
                      }
                    
                      public java.awt.Image createImage2(final javafx.scene.image.Image image) {
                        final int DELAY = 20;
                        final int width = (int) image.getWidth();
                        final int height = (int) image.getHeight();
                        final BufferedImage awtImage = new BufferedImage(width, height,
                                BufferedImage.TYPE_INT_ARGB);
                        final Graphics g = awtImage.createGraphics();
                        ImageView imageView = new ImageView(image);
                        final BorderPane borderPane = new BorderPane();
                        borderPane.setCenter(imageView);
                        final JFXPanel panel = new JFXPanel();
                        panel.setSize(width, height);
                        imageReady = false;
                        KeyFrame keyFrame0 = new KeyFrame(Duration.ZERO,
                                new EventHandler<javafx.event.ActionEvent>() {
                    
                                  @Override
                                  public void handle(javafx.event.ActionEvent event) {
                                    Scene scene = new Scene(borderPane, width, height);
                                    panel.setScene(scene);
                                  }
                                },
                                null);
                        KeyFrame keyFrame1 = new KeyFrame(Duration.valueOf(DELAY),
                                new EventHandler<javafx.event.ActionEvent>() {
                    
                                  @Override
                                  public void handle(javafx.event.ActionEvent event) {
                                    panel.paint(g);
                                    g.dispose();
                                    imageReady = true;
                                  }
                                },
                                null);
                        Timeline timeline = new Timeline();
                        timeline.getKeyFrames().addAll(keyFrame0, keyFrame1);
                        timeline.play();
                        while (!imageReady) {
                          try {
                            Thread.sleep(DELAY);
                          } catch (InterruptedException ex) {
                            ex.printStackTrace();
                          }
                        }
                        return awtImage;
                      }
                    }
                    <tt>ImageConverterTest</tt>
                    import java.awt.image.BufferedImage;
                    import java.io.File;
                    import java.io.IOException;
                    import javafx.application.Application;
                    import javafx.application.Platform;
                    import javafx.event.ActionEvent;
                    import javafx.event.EventHandler;
                    import javafx.geometry.Insets;
                    import javafx.scene.Scene;
                    import javafx.scene.control.Button;
                    import javafx.scene.image.ImageView;
                    import javafx.scene.layout.BorderPane;
                    import javafx.scene.layout.HBox;
                    import javafx.scene.layout.Pane;
                    import javafx.stage.Stage;
                    import javafx.stage.WindowEvent;
                    import javax.imageio.ImageIO;
                    import javax.swing.ImageIcon;
                    import javax.swing.JOptionPane;
                    
                    public class ImageConverterTest extends Application {
                    
                      public static void main(String[] args) {
                        Application.launch(args);
                      }
                    
                      @Override
                      public void start(Stage stage) throws Exception {
                        Button awt2fxButton = new Button("Test AWT to FX");
                        awt2fxButton.setOnAction(new EventHandler<ActionEvent>() {
                    
                          @Override
                          public void handle(ActionEvent event) {
                            try {
                              BufferedImage awtImage = ImageIO.read(new File("E:/path/to/image/imagefile.jpg"));
                              final javafx.scene.image.Image fxImage = ImageConverter.createImage(awtImage);
                              Platform.runLater(new Runnable() {
                    
                                @Override
                                public void run() {
                                  ImageView imageView = new ImageView(fxImage);
                                  BorderPane borderPane = new BorderPane();
                                  borderPane.setCenter(imageView);
                                  Scene scene = new Scene(borderPane, fxImage.getWidth(), fxImage.getHeight());
                                  Stage stage = new Stage();
                                  stage.setScene(scene);
                                  stage.setVisible(true);
                                }
                              });
                            } catch (IOException ex) {
                              ex.printStackTrace();
                            }
                          }
                        });
                        Button fx2awtButton = new Button("Test FX to AWT");
                        fx2awtButton.setOnAction(new EventHandler<ActionEvent>() {
                    
                          @Override
                          public void handle(ActionEvent event) {
                            final javafx.scene.image.Image fxImage =
                                    new javafx.scene.image.Image("E:/path/to/image/imagefile.jpg");
                            new Thread(new Runnable() {
                    
                              @Override
                              public void run() {
                                java.awt.Image awtImage = new ImageConverter().createImage(fxImage);
                                JOptionPane.showMessageDialog(null, new ImageIcon(awtImage));
                              }
                            }).start();
                          }
                        });
                        Button fx2awt2Button = new Button("Test FX to AWT (2)");
                        fx2awt2Button.setOnAction(new EventHandler<ActionEvent>() {
                    
                          @Override
                          public void handle(ActionEvent event) {
                            final javafx.scene.image.Image fxImage =
                                    new javafx.scene.image.Image("E:/path/to/image/imagefile.jpg");
                            new Thread(new Runnable() {
                    
                              @Override
                              public void run() {
                                java.awt.Image awtImage = new ImageConverter().createImage2(fxImage);
                                JOptionPane.showMessageDialog(null, new ImageIcon(awtImage));
                              }
                            }).start();
                          }
                        });
                    
                        Pane panel = new HBox(10);
                        panel.setPadding(new Insets(10, 10, 10, 10));
                        panel.getChildren().addAll(awt2fxButton, fx2awtButton, fx2awt2Button);
                    
                        Scene scene = new Scene(panel);
                        stage.setScene(scene);
                        stage.setOnHidden(new EventHandler<WindowEvent>() {
                    
                          @Override
                          public void handle(WindowEvent event) {
                            System.exit(0);
                          }
                        });
                        stage.setVisible(true);
                      }
                    }
                    Output image from FX to file is never saved on HDD or i did not notice it.
                    No, it's not saved, and that wasn't the intention. ImageIO is used to write only to a stream buffer which can be read by the fx.Image constructor.
                    About those 2 seconds of waiting i used 200ms it works even with 5ms did not try below 5,
                    I've reduced mine to 20 ms. I'm a bit concerned about whether that would still result in a blank image on a slow computer. It would be cleaner if we could hook into the completeness of the image display process, something like (not really) the AWT ImageObserver.
                    As i said up there when i close stage in your app process still remains, here i was paying attention to close/dispose everything so when u close main stage there is no process that still runs.
                    I need to look at that part of it closely and see what I'm (still) doing wrong. For the time being, I've introduced a brute-force <tt>System.exit(0);</tt>
                    I tried to minimize it and lower the delay to 5 ...
                    My code no longer uses a JFrame. It isn't really needed for painting a Component to a BufferedImage.

                    Thanks again, db
                    • 7. Re: Image conversion between AWT and FX
                      DarrylBurke
                      So far, this is the shortest working solution I could create.
                        public static java.awt.Image createImage3(final javafx.scene.image.Image image) {
                          final int width = (int) image.getWidth();
                          final int height = (int) image.getHeight();
                          
                          final BufferedImage awtImage = new BufferedImage(width, height,
                                  BufferedImage.TYPE_INT_ARGB);
                          final Graphics g = awtImage.createGraphics();
                          
                          ImageView imageView = new ImageView(image);
                          final BorderPane borderPane = new BorderPane();
                          borderPane.setCenter(imageView);
                          final JFXPanel panel = new JFXPanel();
                          panel.setSize(width, height);
                          Platform.runLater(new Runnable() {
                      
                            @Override
                            public void run() {
                              Scene scene = new Scene(borderPane, width, height);
                              panel.setScene(scene);
                            }
                          });
                          try {
                            Thread.sleep(20);
                          } catch (InterruptedException ex) {
                            ex.printStackTrace();
                          }
                          SwingUtilities.invokeLater(new Runnable() {
                      
                            @Override
                            public void run() {
                              panel.paint(g);
                              g.dispose();
                            }
                          });
                          return awtImage;
                        }
                      }
                      The method must however be called on a background <tt>Thread</tt>, which limits its usability. My test code:
                      new Thread(new Runnable() {
                      
                        @Override
                        public void run() {
                          java.awt.Image awtImage = ImageConverter.createImage3(fxImage);
                          JOptionPane.showMessageDialog(null, new ImageIcon(awtImage));
                        }
                      }).start();
                      db
                      • 8. Re: Image conversion between AWT and FX
                        MiPa
                        I have to admit that I did not read the full thread so please forgive me if I have just missed the point but I saw some strange methods in here to convert between image formats (awt/fx) and I just wonder whether you have experimented with any of the following methods which seem to be much more straigth forward to me.
                        import java.awt.image.BufferedImage;
                        
                        import javafx.scene.image.Image;
                        
                        public class ImageConverter {
                             
                            public static javafx.scene.image.Image convertToFxImage(java.awt.image.BufferedImage awtImage) {
                                 if (Image.impl_isExternalFormatSupported(BufferedImage.class)) {
                                      return javafx.scene.image.Image.impl_fromExternalImage(awtImage);
                                 } else {
                                      return null;
                                 }
                            }
                            
                            public static java.awt.image.BufferedImage convertToAwtImage(javafx.scene.image.Image fxImage) {
                                 if (Image.impl_isExternalFormatSupported(BufferedImage.class)) {              
                                      java.awt.image.BufferedImage awtImage = new BufferedImage((int)fxImage.getWidth(), (int)fxImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
                                     return (BufferedImage)fxImage.impl_toExternalImage(awtImage);
                                 } else {
                                      return null;
                                 }
                            }
                        
                        }
                        • 9. Re: Image conversion between AWT and FX
                          jojorabbit
                          Hi,
                          Nice, it seems ok to me it works and there is no process running that lefts when you close stage.
                          I hope that developers won't completely remove those impl_XXX methods.
                          But i still like the way with jframe so i can save whole node to image file not just image.
                          Thanks.
                          • 10. Re: Image conversion between AWT and FX
                            DarrylBurke
                            jojorabbit wrote:
                            I hope that developers won't completely remove those impl_XXX methods.
                            The <tt>impl_</tt> methods are not part of the public API and won't remain. The best we can hope for is new public methods with the same functionality ... if<tt> </tt>the API hasn't yet been 'frozen'.

                            db
                            • 11. Re: Image conversion between AWT and FX
                              swpalmer
                              Is there no sane way to do this?

                              The above are good hacks for low-throughput cases, but I need to show a sequence of images as fast as possible. I have raw RGB data and I need to get it on the screen. In Swing/AWT this was easy enough. In JavaFX there seems to be no way to do it. My current application is producing a stream of BufferedImages - getting to the screen was a simple paint on the graphics context. Now with JavaFX.. the framework that is supposed to be rich with multimedia features now makes it next to impossible. I'm using a JFXPanel inside a Swing app for the moment as we are migrating our UI. The main interface for the user is done in FX.
                              • 12. Re: Image conversion between AWT and FX
                                Richard Bair-Oracle
                                Hi, couple things. What is the approach you are using, and what is the actual throughput you are seeing?

                                Second, Jasper had something he started last week, but he's on vacation this week. The idea is that there is already a mechanism in place for media where it decodes into a texture and passes that texture off to prism which avoids having to do any buffer copies. Likewise we'd like to provide some kind of API in javafx.embed.swing which will allow you to have a Node which basically has a Graphics2D based paint method on it along with some invalidate(rect) type methods, and then it will handle the buffer for you and you can just draw away. We expect it to be a really easy thing to do, but like I said he's on vacation this week so I'm not sure how far he got.

                                Richard
                                • 13. Re: Image conversion between AWT and FX
                                  swpalmer
                                  Hi Richard,

                                  I'm not using any approach yet... I was just about to port this part of my UI from something that was based on NetBeans Visual Library. I started to poke around with a decompiler, since obviously at some point you have a bunch of pixel data that you got from decoding those input streams. I was going to see if I could jump into the middle somehow to get my raster data into a javafx.scene.Image. I find it extremely short-sighted that the only way JavaFX can generate a bitmap image is from a file. That's just bizarre.

                                  I would like to get throughput of 60 frames per second at least.. the app has a lot of other things to do, showing a preview image isn't supposed to take much CPU power. (Though it does take a little too much for the Swing implementation as it is now).

                                  I'm not interested (yet) in going from a JavaFX Image back to an AWT Image, though I have to admit that getting a raster that you can do something useful with is important for JavaFX as well. Interoperability with ImageIO would be nice.

                                  What you are saying about a mechanism for media going into a texture without additional buffer copies sounds very promising. I complained years ago (JavaFX 1.0) that the media system really needed to be extensible such that we could integrate our own media codecs either with pure Java or native code. (That would also go towards shutting up those that complained about the use of the obsolete VP6 codec - we could use an h.264 codec that we licensed ourselves if we wanted).

                                  I'm very interested in these developments as we are basing a high-end media processing application on Java and JavaFX.
                                  • 14. Re: Image conversion between AWT and FX
                                    Richard Bair-Oracle
                                    Hi!
                                    swpalmer wrote:
                                    Hi Richard,

                                    I'm not using any approach yet... I was just about to port this part of my UI from something that was based on NetBeans Visual Library. I started to poke around with a decompiler, since obviously at some point you have a bunch of pixel data that you got from decoding those input streams. I was going to see if I could jump into the middle somehow to get my raster data into a javafx.scene.Image. I find it extremely short-sighted that the only way JavaFX can generate a bitmap image is from a file. That's just bizarre.
                                    Actually I might say it was too far-sighted :-). I guess short-sighted would mean we weren't thinking far enough ahead, whereas in this case I guess we were thinking too far ahead, and didn't manage to provide something clean on day 1. The main reason that we didn't do the obvious thing and provide an API that allowed for converting from BufferedImage to javafx Image is that we cannot have any API tied to AWT / Swing / SWT except in the embed package. To be honest, it wasn't until just a couple weeks back at Devoxx that Jasper and I looked at each other and realized how stupid we were that we didn't think of providing a custom node in the javafx.embed.swing package that was capable of doing 2D graphics. We had been planning (from before 1.0) to add a "binary node" which would be the basis for doing pixel bashing type operations, and we wanted to have a Canvas node that was basically web Canvas ported to JavaFX. And those things are a bit more complicated and require coordination among multiple parts of the code and the teams involved are also bashing their brains out trying to get Mac and Linux support, so just shear resource constraints are the problem.

                                    But then we thought we were freaking idiots for not just doing a Canvas2D Node in the embed package that had normal AWT APIs. In the first instance it could be dumb and just do an image copy, and then get upgraded to do direct texture stuff (there is some additional complication there, notably, the splitting of the rendering and event threads, but anyway). In any case it should be more than fast enough. So that's kind of where we're at.
                                    I would like to get throughput of 60 frames per second at least.. the app has a lot of other things to do, showing a preview image isn't supposed to take much CPU power. (Though it does take a little too much for the Swing implementation as it is now).

                                    I'm not interested (yet) in going from a JavaFX Image back to an AWT Image, though I have to admit that getting a raster that you can do something useful with is important for JavaFX as well. Interoperability with ImageIO would be nice.

                                    What you are saying about a mechanism for media going into a texture without additional buffer copies sounds very promising. I complained years ago (JavaFX 1.0) that the media system really needed to be extensible such that we could integrate our own media codecs either with pure Java or native code. (That would also go towards shutting up those that complained about the use of the obsolete VP6 codec - we could use an h.264 codec that we licensed ourselves if we wanted).

                                    I'm very interested in these developments as we are basing a high-end media processing application on Java and JavaFX.
                                    As for the codecs, you may be interested in the latest 2.0.2 developments (shipping here as GA in a week or so). We've pulled out the Thompson codec so that JavaFX is no redistributable. To play MP3, we're delegating to the platform codec (all using Gstreamer).
                                    1 2 Previous Next