开发者

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

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 开发者_JAVA技巧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?


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


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");


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜