Dear all,
I would like to submit a patch that corrects ImageJ's behavior when running in headless mode (i.e. with java -Djava.awt.headless=true). Symptoms: When operating in headless mode ( i.e. java - Djava.awt.headless=true-jar MyConverter.jar ), ImageJ is unable to perform a successful call to ij.processor.ImageProcessor.drawString() because drawString() calls the setupFrame() function, which in turn attempts to instanciate a java.awt.Frame object. This attempt results in a HeadlessException error being thrown by the java.awt.Frame constructor (there isn't much sense for Java to create a frame if a display server is not present, so it throws an exception error). The mistake here is that functions that do image manipulation, like drawString(), and do not require the ImageJ GUI, should not call setupFrame() if Java is running in headless mode. Solution: The only reason drawString() calls setupFrame() is to create valid Font and FontMetrics objects, and the only reason setupFrame() creates a Frame object is to get a Graphics object that is used to create the FontMetrics object. If it were possible to get the Graphics object from a different source, not java.awt.Frame, then there would be no need to create the Frame. And thus, I have modified the setupFrame() function to use a BufferedImage object, only in case the java.awt.headless property is true. Below is a modified version of the setupFrame() function, contained in ij.process.ImageProcessor.java (revision 1.37v): -------------- v.137v ---------------------- private void setupFrame() { if (System.getProperty("java.awt.headless").equalsIgnoreCase("true")) { if (image==null) image=new BufferedImage(img.getWidth(null),img.getHeight (null),BufferedImage.TYPE_INT_RGB); if (font==null) font = new Font("SansSerif", Font.PLAIN, 12); if (fontMetrics==null) { Graphics g=image.getGraphics(); fontMetrics=g.getFontMetrics(font); } return; } if (frame==null) { frame = new Frame(); frame.pack(); frame.setBackground(Color.white); } if (font==null) font = new Font("SansSerif", Font.PLAIN, 12); if (fontMetrics==null) { frame.setFont(font); fontMetrics = frame.getFontMetrics(font); } } ---------------------------------------------------------- I have attached the modified source code of ImageProcessor.java. Conclusion: This approach allowed us, the developers at the Center for Brain Science and Neuroimaging at Harvard, to continue using our image viewer/converter on Linux computers that do not have the X server installed. Currently, we are forced to maintain a separate version of ImageJ that incorporates the changes explained above. We would like this fix to be applied by the ImageJ maintainers, or, at least, for the setupFrame() problem to be resolved, so that we could use new versions of ImageJ in the future, without worrying about modifying and rebuilding from source. Sincerely, Victor Petrov ImageProcessor.java.txt (66K) Download Attachment |
Hi,
On Wed, 18 Jul 2007, Victor Petrov wrote: > I would like to submit a patch that corrects ImageJ's behavior when > running in headless mode (i.e. with java -Djava.awt.headless=true). Great! > I have attached the modified source code of ImageProcessor.java. Here is a patch, made from your file diff'ed vs 1.37u, and my comments interspersed (a patch shows so called "hunks", where a space at the beginning of the line means that both old and new versions have this line, a "+" means that only the new version has it, and "-" means that only the old version has it): diff --git a/ij/process/ImageProcessor.java b/ij/process/ImageProcessor.java index ebbc201..911853e 100644 --- a/ij/process/ImageProcessor.java +++ b/ij/process/ImageProcessor.java @@ -3,6 +3,7 @@ package ij.process; import java.util.*; import java.awt.*; import java.awt.image.*; +import java.awt.FontMetrics; import java.lang.reflect.*; import ij.gui.*; import ij.util.Java2; @@ -44,6 +45,7 @@ public abstract class ImageProcessor extends Object { protected boolean antialiasedText; protected boolean boldFont; static Frame frame; + static Graphics graphics; ProgressBar progressBar; boolean pixelsModified; @@ -856,6 +858,21 @@ public abstract class ImageProcessor extends Object { private ImageProcessor dotMask; private void setupFrame() { + + if (System.getProperty("java.awt.headless").equalsIgnoreCase("true")) + { + if (image==null) + image=new BufferedImage(img.getWidth(null),img.getHeight(null),BufferedImage.TYPE_INT_RGB); + + if (font==null) + font = new Font("SansSerif", Font.PLAIN, 12); + if (fontMetrics==null) + { + Graphics g=image.getGraphics(); + fontMetrics=g.getFontMetrics(font); + } + return; + } What about the variable "graphics"? if (frame==null) { frame = new Frame(); frame.pack(); @@ -873,9 +890,12 @@ public abstract class ImageProcessor extends Object { public void drawString(String s) { if (s.equals("")) return; + //If running in headless mode, setupFrame generates HeadlessException, + //therefore setupFrame should only run when java.awt.headless is not defined setupFrame(); if (ij.IJ.isMacOSX()) s += " "; + int w = getStringWidth(s); int cxx = cx; if (justification==CENTER_JUSTIFY) @@ -885,10 +905,11 @@ public abstract class ImageProcessor extends Object { int h = fontMetrics.getHeight(); if (w<=0 || h<=0) return; Image img; - if (ij.IJ.isLinux() && ij.IJ.isJava2()) + if ((ij.IJ.isLinux() && ij.IJ.isJava2()) || (System.getProperty("java.awt.headless").equalsIgnoreCase("true"))) img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); else img = frame.createImage(w, h); + Graphics g = img.getGraphics(); FontMetrics metrics = g.getFontMetrics(font); int fontHeight = metrics.getHeight(); @@ -972,7 +993,10 @@ public abstract class ImageProcessor extends Object { setupFrame(); int w; if (antialiasedText) { - Graphics g = frame.getGraphics(); + //Graphics g = frame.getGraphics(); + //-BEGIN-CHANGE + Graphics g=graphics; + //-END-CHANGE I do not see any code that could possibly set the variable "graphics". Have you tested this with antialiasedText==true? But it is good that someone is working on that. Thank you very much! Ciao, Dscho |
Dear Johannes,
Thank you for pointing that out. Originally, setupFrame() was using the global variable "graphics", which was a placeholder for the Graphics object. I guess in the process of testing, I changed setupFrame() to use a local Graphics g variable and forgot to change it back. I wasn't using the Antialiased option so I didn't have any issues due to graphics being null. I've changed the setupFrame function to use the global "graphics" variable and here's the patch for it (apparently, my diff doesn't support the --git option): ------------------------------------------------------------------------------------------------------------------------------------------------------------ $ diff -Naur ../ImageJ-Source-1.37v/ij/process/ImageProcessor.java ij/process/ImageProcessor.java ------------------------------------------------------------------------------------------------------------------------------------------------------------ --- original/ij/process/ImageProcessor.java 2006-08-24 16:09: 16.000000000 -0400 +++ new/ij/process/ImageProcessor.java 2007-07-18 23:09:34.000000000-0400 @@ -3,6 +3,7 @@ import java.util.*; import java.awt.*; import java.awt.image.*; +import java.awt.FontMetrics; import java.lang.reflect.*; import ij.gui.*; import ij.util.Java2; @@ -44,6 +45,7 @@ protected boolean antialiasedText; protected boolean boldFont; static Frame frame; + static Graphics graphics; ProgressBar progressBar; boolean pixelsModified; @@ -856,6 +858,21 @@ private ImageProcessor dotMask; private void setupFrame() { + + if (System.getProperty("java.awt.headless ").equalsIgnoreCase("true")) + { + if (image==null) + image=new BufferedImage(img.getWidth(null), img.getHeight(null),BufferedImage.TYPE_INT_RGB); + + if (font==null) + font = new Font("SansSerif", Font.PLAIN, 12); + if (fontMetrics==null) + { + graphics=image.getGraphics(); + fontMetrics=graphics.getFontMetrics(font); + } + return; + } if (frame==null) { frame = new Frame(); frame.pack(); @@ -873,9 +890,12 @@ public void drawString(String s) { if (s.equals("")) return; + //If running in headless mode, setupFrame generates HeadlessException, + //therefore setupFrame should only run when java.awt.headless is not defined setupFrame(); if (ij.IJ.isMacOSX()) s += " "; + int w = getStringWidth(s); int cxx = cx; if (justification==CENTER_JUSTIFY) @@ -885,10 +905,11 @@ int h = fontMetrics.getHeight(); if (w<=0 || h<=0) return; Image img; - if (ij.IJ.isLinux() && ij.IJ.isJava2()) + if ((ij.IJ.isLinux() && ij.IJ.isJava2()) || ( System.getProperty("java.awt.headless").equalsIgnoreCase("true"))) img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); else img = frame.createImage(w, h); + Graphics g = img.getGraphics(); FontMetrics metrics = g.getFontMetrics(font); int fontHeight = metrics.getHeight(); @@ -972,7 +993,10 @@ setupFrame(); int w; if (antialiasedText) { - Graphics g = frame.getGraphics(); + //Graphics g = frame.getGraphics(); + //-BEGIN-CHANGE + Graphics g=graphics; + //-END-CHANGE if (g==null) { frame = null; setupFrame(); ----------------------------------------------------------------------------------- I hope this helps. Sincerely, Victor Petrov |
Hi Victor,
On Wed, 18 Jul 2007, Victor Petrov wrote: > Thank you for pointing that out. Originally, setupFrame() was using the > global variable "graphics", which was a placeholder for the Graphics > object. I guess in the process of testing, I changed setupFrame() to use > a local Graphics g variable and forgot to change it back. > > I wasn't using the Antialiased option so I didn't have any issues due to > graphics being null. That's what I suspected. > I've changed the setupFrame function to use the global "graphics" > variable and here's the patch for it Thank you for your work! > (apparently, my diff doesn't support the --git option): Heh. I use "git" to track source code, and it was much easier to convince git to just pick out the right version of ImageProcessor.java, so that I could see what _you_ changed, as opposed to what was changed in the recent releases. IMHO a diff shows better what you did, and is nicer to discuss things via email, but I know that Wayne likes complete files, when it comes to inclusion. Wayne, if you want to see the diff against the current release, please see: http://repo.or.cz/w/imageja.git?a=commitdiff;h=e5dc6a95b4722c6b00b8b07cecfac820dac3fd10 and for the complete file: http://repo.or.cz/w/imageja.git?a=blob_plain;f=ij/process/ImageProcessor.java;h=da739933f29e5898b8892643fe47639c462de001 Ciao, Dscho |
In reply to this post by Victor Petrov
This bug is fixed in ImageJ 1.39a. The v1.39a daily build is at
<http://rsb.info.nih.gov/ij/ij.jar>, the source is at <http://rsb.info.nih.gov/ij/source/>, and the release notes are at <http://rsb.info.nih.gov/ij/source/release-notes.html>. -wayne On Jul 18, 2007, at 5:32 PM, Victor Petrov wrote: > Dear all, > > I would like to submit a patch that corrects ImageJ's behavior when > running > in headless mode (i.e. with java -Djava.awt.headless=true). > > Symptoms: > When operating in headless mode ( i.e. java - > Djava.awt.headless=true-jar > MyConverter.jar ), ImageJ is unable to perform a successful call to > ij.processor.ImageProcessor.drawString() because drawString() calls the > setupFrame() function, which in turn attempts to instanciate a > java.awt.Frame object. > This attempt results in a HeadlessException error being thrown by the > java.awt.Frame constructor (there isn't much sense for Java to create a > frame if a display server is not present, so it throws an exception > error). > The mistake here is that functions that do image manipulation, like > drawString(), and do not require the ImageJ GUI, should not call > setupFrame() if Java is running in headless mode. > > Solution: > The only reason drawString() calls setupFrame() is to create valid > Font > and FontMetrics objects, and the only reason setupFrame() creates a > Frame > object is to get a Graphics object that is used to create the > FontMetrics > object. If it were possible to get the Graphics object from a different > source, not java.awt.Frame, then there would be no need to create the > Frame. > And thus, I have modified the setupFrame() function to use a > BufferedImage > object, only in case the java.awt.headless property is true. > Below is a modified version of the setupFrame() function, contained in > ij.process.ImageProcessor.java (revision 1.37v): > > -------------- v.137v ---------------------- > private void setupFrame() { > > if > (System.getProperty("java.awt.headless").equalsIgnoreCase("true")) > > { > if (image==null) > image=new BufferedImage(img.getWidth(null),img.getHeight > (null),BufferedImage.TYPE_INT_RGB); > > if (font==null) > font = new Font("SansSerif", Font.PLAIN, 12); > if (fontMetrics==null) > { > Graphics g=image.getGraphics(); > fontMetrics=g.getFontMetrics(font); > } > return; > } > if (frame==null) { > frame = new Frame(); > frame.pack(); > frame.setBackground(Color.white); > } > if (font==null) > font = new Font("SansSerif", Font.PLAIN, 12); > if (fontMetrics==null) { > frame.setFont(font); > fontMetrics = frame.getFontMetrics(font); > } > } > ---------------------------------------------------------- > > I have attached the modified source code of ImageProcessor.java. > > Conclusion: > This approach allowed us, the developers at the Center for Brain > Science > and Neuroimaging at Harvard, to continue using our image > viewer/converter on > Linux computers that do not have the X server installed. > Currently, we are forced to maintain a separate version of ImageJ that > incorporates the changes explained above. > We would like this fix to be applied by the ImageJ maintainers, or, at > least, for the setupFrame() problem to be resolved, so that we could > use new > versions of ImageJ in the future, without worrying about modifying and > rebuilding from source. > > Sincerely, > Victor Petrov > <ImageProcessor.java.txt> |
Free forum by Nabble | Edit this page |