Save a Processing sketch to a PNG file, server-side with no GUI/display
Asked Answered
P

3

7

I'd like to use Processing to render a visualization on the server side (headlessly, with no GUI). The Processing sketch is static (i.e. does not animate), so I only need to grab the first frame, and I'd like to serve this result out to the users of our web application on-demand.

I've searched around a bit on the processing.org forums and it's been suggested that Processing is not intended to be launched headlessly. The only hack I've seen to do it is one involving launching a headless X11 display:

Xvfb :2 &
export DISPLAY=":2"
./myapp
killall -9 Xvfb

.. Which is not going to work for us as we'd like to have a pure-Java solution and can't always guarantee an X renderer on the server-side.

How do I do this in pure Java?

Paine answered 22/6, 2010 at 14:9 Comment(0)
M
1

Xvfb is likely to be faster than a java renderer, and a hardware-accelerated X server will be the fastest by a large margin, but if you want a 'pure' java solution you could try the Pure Java AWT Toolkit.

EDIT: Here's a boot command line example lifted from here:

java -Xbootclasspath:JDK/jre/lib/rt.jar:LIB/pja.jar -Dawt.toolkit=com.eteks.awt.PJAToolkit -Djava.awt.graphicsenv=com.eteks.java2d.PJAGraphicsEnvironment -Djava.awt.fonts=JDK/jre/lib/fonts mainclassname args
Metaplasia answered 22/6, 2010 at 14:23 Comment(4)
This 3 year old thread on processing.org says that PJA is not compatible with Processing: processing.org/discourse/yabb2/YaBB.pl?num=1193317878 . Any thoughts on whether this has progressed at all?Paine
When I've had this problem I just bit the bullet and installed an X server. This is the fastest and most compatible - and why make the situation more difficult than it already is? In my company we spent a year trying to build a 'pure java' solution which we then threw out the window since it couldn't deal with all the corner cases. You got to use the right tool for the job. I suggest you go for X, xvfb if you don't have a graphics card on the box.Metaplasia
Xvfb would be fine for our Linux server, but some of our development boxes are Windows-based. Is it possible to run Xvfb on Cygwin?Paine
Can you even run windows headless? I can imagine that you could get some sort of problem if you are running as a service, due to the restricted access to the user graphics environment, but I wouldn't expect so. Are you running into this problem?Metaplasia
V
1

Create a standard headless Java app, create a PGraphics object in it(1) and perform all of your drawing operations on that. Then save the PGraphics object to disk as an image file using .save().

1 You may need to obtain this from a PApplet, I'm not sure if you can create it directly.

The code will look mode or less like this:

PApplet applet = new PApplet();
PGraphics g = applet.createGraphics(200, 400, PApplet.JAVA2D) // same params as size()
g.beginDraw();
g.ellipse // ... etc, your drawing goes here
g.endDraw();
g.save("filename.png");
Virology answered 11/5, 2011 at 19:43 Comment(0)
M
0

The solution from Ollie Glass ceased to work because the constructor of PApplet/Applet checks whether the environment is headless or not, i.e. -Djava.awt.headless=true.

So there is no way of creating a PApplet object in the first place.

Instead, create your PGraphics directly. For instance, to draw everything into a pdf

PGraphics pdf = new PGraphicsPDF();
pdf.setPrimary(false);
pdf.setPath(filename);
pdf.setSize(sizeX, sizeY);
// pdf.setParent(new PApplet()); This is intentionally NOT called.

pdf.beginDraw();

// draw everything

pdf.dispose();
pdf.endDraw();

Adding text will still throw an exception since the underlying PGraphics calls its parent (the PApplet) for some helper methods. However, this hasn't been set because we are not allowed to create a PApplet in the first place.

A solution is to get rid of these function calls is creating you own version of PGraphicsPDF. For example

class MyPGraphicsPDF extends PGraphicsPDF{

    @Override
    public float textAscent() {
        if (textFont == null) {
          defaultFontOrDeath("textAscent");
        }

        Font font = (Font) textFont.getNative();
        //if (font != null && (textFont.isStream() || hints[ENABLE_NATIVE_FONTS])) {
        if (font != null) {
          FontMetrics metrics = this.getFontMetrics(font);
          return metrics.getAscent();
        }
        return super.textAscent();
      }

    @Override
      public float textDescent() {
        if (textFont == null) {
          defaultFontOrDeath("textDescent");
        }
        Font font = (Font) textFont.getNative();
        //if (font != null && (textFont.isStream() || hints[ENABLE_NATIVE_FONTS])) {
        if (font != null) {
          FontMetrics metrics = this.getFontMetrics(font);
          return metrics.getDescent();
        }
        return super.textDescent();
      }

    public FontMetrics getFontMetrics(Font font) {
        FontManager fm = FontManagerFactory.getInstance();
        return sun.font.FontDesignMetrics.getMetrics(font);
    }
}

textAscent() and textDescent() are copies of the code from PGraphics with the change of not calling getFontMetrics(Font font)from the non-existing parent PApplet. Instead both redirect to the third method that reimplements the missing helper method of PApplet as a slightly shorter version of java.awt.Component.getFontMetrics(Font font).

Hope that helps.

Would be nice to have a native headless version of processing when explicitely calling for a file as drawing board.

Mudlark answered 15/10, 2014 at 13:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.