-
Bug
-
Resolution: Duplicate
-
P4
-
None
-
8u20, 8u40
-
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.
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.
- duplicates
-
JDK-8088247 [JFXPanel, Mac Intel Iris Pro GPU] JDialog with JavaFX scene has major performance problem on Mac PowerBook w/retina display
-
- Resolved
-