简体   繁体   中英

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 (ie 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?

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, ie -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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM