NullPointerException when calling save() after surface.setSize() in P2D

#1

Hi all,

I’m getting a NullPointerException when trying to use save() in conjunction with surface.setSize() and the P2D renderer. Here’s a simplified version of my code:

void settings() {
  size(768, 512, P2D);
}

void setup() {
  background(0);
  fill(0);
  stroke(255);
  ellipse(256, 256, 128, 128);
}

void draw() {
  // save("before.png");
  surface.setSize(512, 512);
  save("after.png");
  exit();
}

Using the default renderer seems to work just fine. If I comment out the “, P2D” in the call to size() inside settings(), I get a file called “after.png” with a nicely centered circle; and if I also uncomment the call to save(“before.png”), I get another file with a correctly off-center circle.

Using the P2D renderer, the call to save(“after.png”) gives the following errors:

java.lang.NullPointerException
at java.awt.image.BufferedImage.setRGB(BufferedImage.java:1058)
at processing.core.PImage.saveImageIO(PImage.java:3229)
at processing.core.PImage.save(PImage.java:3406)
at processing.core.PGraphics.save(PGraphics.java:8399)
at processing.opengl.PGraphicsOpenGL.saveImpl(PGraphicsOpenGL.java:762)
at processing.opengl.PGraphicsOpenGL.save(PGraphicsOpenGL.java:5573)
at processing.core.PApplet.save(PApplet.java:3933)
at e45.draw(e45.java:31)
at processing.core.PApplet.handleDraw(PApplet.java:2475)
at processing.opengl.PSurfaceJOGL$DrawListener.display(PSurfaceJOGL.java:866)
at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:692)
at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:674)
at jogamp.opengl.GLAutoDrawableBase$2.run(GLAutoDrawableBase.java:443)
at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1293)
at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:1147)
at com.jogamp.newt.opengl.GLWindow.display(GLWindow.java:759)
at com.jogamp.opengl.util.AWTAnimatorImpl.display(AWTAnimatorImpl.java:81)
at com.jogamp.opengl.util.AnimatorBase.display(AnimatorBase.java:452)
at com.jogamp.opengl.util.FPSAnimator$MainTask.run(FPSAnimator.java:178)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
Error while saving image.
java.io.IOException: image save failed.
at processing.core.PImage.saveImageIO(PImage.java:3275)
at processing.core.PImage.save(PImage.java:3406)
at processing.core.PGraphics.save(PGraphics.java:8399)
at processing.opengl.PGraphicsOpenGL.saveImpl(PGraphicsOpenGL.java:762)
at processing.opengl.PGraphicsOpenGL.save(PGraphicsOpenGL.java:5573)
at processing.core.PApplet.save(PApplet.java:3933)
at e45.draw(e45.java:31)
at processing.core.PApplet.handleDraw(PApplet.java:2475)
at processing.opengl.PSurfaceJOGL$DrawListener.display(PSurfaceJOGL.java:866)
at jogamp.opengl.GLDrawableHelper.displayImpl(GLDrawableHelper.java:692)
at jogamp.opengl.GLDrawableHelper.display(GLDrawableHelper.java:674)
at jogamp.opengl.GLAutoDrawableBase$2.run(GLAutoDrawableBase.java:443)
at jogamp.opengl.GLDrawableHelper.invokeGLImpl(GLDrawableHelper.java:1293)
at jogamp.opengl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:1147)
at com.jogamp.newt.opengl.GLWindow.display(GLWindow.java:759)
at com.jogamp.opengl.util.AWTAnimatorImpl.display(AWTAnimatorImpl.java:81)
at com.jogamp.opengl.util.AnimatorBase.display(AnimatorBase.java:452)
at com.jogamp.opengl.util.FPSAnimator$MainTask.run(FPSAnimator.java:178)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)

Interestingly enough, if I uncomment the call to save(“before.png”) while still using the P2D renderer, it creates a correct-looking “before.png”, and it doesn’t crash when saving “after.png”, but the resulting file looks like it has been drawn on an incorrectly-sized window:

I’m using Processing 3.5.3 with MacOS 10.13.6.
Please let me know if there’s other data I can gather to help diagnose the problem.

1 Like

#2

I confirm I can reproduce this issue when using Windows 10.

Quick notes:

  • If you remove exit() when using the P2D, it works but you get the last picture that you show above which seems to be a bug.
  • If you remove both resize and exit, it works as intended

My advise: Don’t call exit or resize the way you do.

Second advise: Create a github issue in the Processing repo to report this problem. I would say it is a bug and it should be documented and hopefully somebody will address it.

Kf

1 Like

#3

Glad to hear it gives you similar results… On my system, when I remove the call to exit(), the draw() loop just gives me the same NPE over and over, so that doesn’t seem to help. And I raised the issue because I need to call resize.

So I’ve filed a bug (#5840) in the Processing repo, and now I’ll hope someone can take a look at it. Thanks for the advice.

1 Like

#4

This is a bug for the P2D renderer. However, fixing this does not mean you should use it that way. I wouldn’t. I mean, you are resizing your main canvas twice per drawing cycle. This is like saying your display hardware is able to chance its display settings 60 times per second.

If you explain your use case, somebody in the community could share alternative options.

Thank you for opening the bug.

Kf

0 Likes

#5

The real code that I’m working on is a series of drawing exercises, where I have one simple API and I’m coming up with many implementations of it. The program that hit this bug is a helper for those exercises that lets me select four nice-looking renderings of each implementation and then save the results to a file. I do this by creating a rectangular window, with space for four selections in the left/center and the current candidate on the right, and use key presses to make the four selections. Once I’m happy with them, I press another key; this resizes the window so it just has enough space for the selected drawings, calls save(), and exits.

In short, my actual program only resizes once, from a keyPressed() callback, and it does so immediately before exiting. If there are better ways of doing it, I’d be happy to hear them, but this didn’t seem too bad to me.

0 Likes

#6

You could use states. Based on the state, draw() performs an action.

  • State 1: draw on a sketch with size A x B
  • State 2: resize sketch and draw on sketch with size C x D
  • State 3: exit

Kf

1 Like

#7

Processing.org/reference/saveFrame_.html

0 Likes

#8

Are you resizing the window to make it big enough for the selections, or small enough to crop them? … In either case, you don’t need to do this. You can create a PGraphics of whatever size you want, draw onto that, and save it – no screen resizing needed.

Or create a PImage and copy part of the canvas onto that – assuming your canvas was big enough and you have already drawn onto the canvas – then save the PImage. You can do that with:

PImage myCrop = get(x, y, w, h);
myCrop.save("myCrop.jpg");
1 Like

#9

@jeremydouglass – I’ve done some drawing onto PGraphics. That’s not my favorite approach because (as far as I can tell) I can’t set the pixel density of a PGraphics, so it doesn’t render as crisply as the default graphics context on my Retina MacBook. If there is a way to do this, I’d love to hear it.

But the idea of using get() + save() is certainly simpler than what I’ve been doing, and seems to work great. Thanks for the suggestion!

0 Likes