Patch for ImageJ that fixes a HeadlessException error

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Patch for ImageJ that fixes a HeadlessException error

Victor Petrov
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
Reply | Threaded
Open this post in threaded view
|

Re: Patch for ImageJ that fixes a HeadlessException error

dscho
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
Reply | Threaded
Open this post in threaded view
|

Re: Patch for ImageJ that fixes a HeadlessException error

Victor Petrov
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
Reply | Threaded
Open this post in threaded view
|

Re: Patch for ImageJ that fixes a HeadlessException error

dscho
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
Reply | Threaded
Open this post in threaded view
|

Re: Patch for ImageJ that fixes a HeadlessException error

Wayne Rasband
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>