Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8096240

JFXPanel.resizePixelBuffer slow on Mac with retina display

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P4 P4
    • None
    • 8u20, 8u40
    • javafx
    • MacOSX with retina display

      I have discovered a problem with JFXPanel which I think relays on resizePixelBuffer method: on my Mac Pro with retina display JFXPanel takes a long time to start up (every time it's shown). I made little change in my component that inherits from JFXPanel and replaced the code in resizePixelBuffer that creates BufferedImage with the following:

      GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
      GraphicsDevice device = env.getDefaultScreenDevice();
      GraphicsConfiguration config = device.getDefaultConfiguration();
      pixelsIm = config.createCompatibleImage(pWidth * newScaleFactor,
                          pHeight * newScaleFactor, Transparency.TRANSLUCENT);

      Actually, I have used reflection and written over addNotify because resizePixelBuffer is private, so my code is as follows:

      private Object getDeclFieldValue(String fldName) {
              try {

                  Field fld = JFXPanel.class.getDeclaredField(fldName);
                  fld.setAccessible(true);
                  return fld.get(this);
              } catch (NoSuchFieldException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              }
              return null;
          }

          private void setDeclFieldValue(String fldName, Object val) {
              try {

                  Field fld = JFXPanel.class.getDeclaredField(fldName);
                  fld.setAccessible(true);
                  fld.set(this, val);
              } catch (NoSuchFieldException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              }
          }

          private void callDeclMethod(String metName) {
              try {
                  Method m = JFXPanel.class.getDeclaredMethod(metName);
                  m.invoke(this);
              } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
              }
          }
          
          private BufferedImage createBuffImage(int pW, int pH) {
              GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
              GraphicsDevice device = env.getDefaultScreenDevice();
              GraphicsConfiguration config = device.getDefaultConfiguration();
              return config.createCompatibleImage(pW,
                      pH, Transparency.TRANSLUCENT);
          }

          private void resizePixelBufferX(int newScaleFactor) {
              int pWidth = (Integer) getDeclFieldValue("pWidth");
              int pHeight = (Integer) getDeclFieldValue("pHeight");
              if ((pWidth <= 0) || (pHeight <= 0)) {
                  setDeclFieldValue("pixelsIm", null);
      // pixelsIm = null;
              } else {
                  BufferedImage oldIm = (BufferedImage) getDeclFieldValue("pixelsIm");
                  BufferedImage newIm = createBuffImage(pWidth * newScaleFactor,
                          pHeight * newScaleFactor);
                  setDeclFieldValue("pixelsIm", newIm);
                  if (oldIm != null) {
                      double ratio = newScaleFactor / (Integer) getDeclFieldValue("scaleFactor");
                      // Transform old size to the new coordinate space.
                      int oldW = (int) Math.round(oldIm.getWidth() * ratio);
                      int oldH = (int) Math.round(oldIm.getHeight() * ratio);

                      Graphics g = newIm.getGraphics();
                      try {
                          g.drawImage(oldIm, 0, 0, oldW, oldH, null);
                      } finally {
                          g.dispose();
                      }
                  }
              }
          }

          @Override
          public void addNotify() {
              Integer sf = (Integer) getDeclFieldValue("scaleFactor");
              BufferedImage oldIm = (BufferedImage) getDeclFieldValue("pixelsIm");
              setDeclFieldValue("pixelsIm", null);
              final EmbeddedWindow stage = (EmbeddedWindow) getDeclFieldValue("stage");
              setDeclFieldValue("stage", null);
              super.addNotify();
              setDeclFieldValue("stage", stage);
              setDeclFieldValue("pixelsIm", oldIm);
              resizePixelBufferX(sf);
              Runnable r = new Runnable() {
                  @Override
                  public void run() {
                      if ((stage != null) && !stage.isShowing()) {
                          stage.show();
                          callDeclMethod("sendMoveEventToFX");
                      }
                  }
              };
              if (Platform.isFxApplicationThread()) {
                  r.run();
              } else {
                  Platform.runLater(r);
              }
          }

      After this I have no problem with this issue. I have tested even on my Windows machine and it does not seem to be a problem there either.

            ant Anton Tarasov (Inactive)
            duke J. Duke
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported: