Saving high resolution images

Once we create a nice image using processing we naturally want to save it.
This is easy done, but saving images at a higher resolution can be a pain.
To simplify this I created a method that i can include in my sketches and save high resolution images at will.

Normal saving

To save images you can simply call the save() function. It requires a filename as argument and will save the currently displayed image. The filename is relative to the location of the sketch file. Pretty straightforward.

save( "output.png" );

High resolution saving

Saving an image at a higher resolution can be a bit more tricky.
We could use a bigger canvas size and a higher line weight then change the position of everything so the image generated is actually bigger but it’s not really practical.
A better thing to do would be to scale the image before saving it.

To do this we will use a PGraphics object to hold an off-screen buffer of a specific size and scale, draw on it and then save it as our high resolution image.

We are going to contain all the saving process in a function. Let’s call it saveHiRes(). It will have one argument: the scale at which the image will be saved.

void saveHiRes(int scaleFactor) {
}

First we create a PGraphics object with the createGraphics() function.
This function needs two arguments, the width and height of the new canvas.
We can give it a third argument to specify the type of renderer used.
These sizes are multiplied by the scaleFactor so the created buffer has the correct size for the scaled drawing.

void saveHiRes(int scaleFactor) {
  PGraphics hires = createGraphics(width*scaleFactor, height*scaleFactor, JAVA2D);
}

To draw on this buffer we need to call our drawing functions between beginRecord() and endRecord().
The beginRecord() has to be given the pGgaphics object so the calls to the drawing functions affect this buffer and not the original canvas.

void saveHiRes(int scaleFactor) {
  PGraphics hires = createGraphics(width*scaleFactor, height*scaleFactor, JAVA2D);
  beginRecord(hires);
  endRecord();
}

Once beginRecord() is called, the buffer is initialized and we can set its scale.

void saveHiRes(int scaleFactor) {
  PGraphics hires = createGraphics(width*scaleFactor, height*scaleFactor, JAVA2D);
  beginRecord(hires);
  hires.scale(scaleFactor);
  endRecord();
}

Now that we have a valid, initialized buffer at the correct size and scale, we can simply call our setup() and draw() functions and they will be executed in the context of this pGraphics object.

void saveHiRes(int scaleFactor) {
  PGraphics hires = createGraphics(width*scaleFactor, height*scaleFactor, JAVA2D);
  beginRecord(hires);
  hires.scale(scaleFactor);
  setup();
  draw();
  endRecord();
}

Our drawing is done. The last thing to do is to save this image by calling the save() method of the pGraphics object.

void saveHiRes(int scaleFactor) {
  PGraphics hires = createGraphics(width*scaleFactor, height*scaleFactor, JAVA2D);
  beginRecord(hires);
  hires.scale(scaleFactor);
  setup();
  draw();
  endRecord();
  hires.save( "output.png" );
}

The saveHiRes() function is done. We can now create our processing sketch as we like and save the output image at a higher resolution.
The size of the the output image will be the size of the original canvas multiplied by the argument given to the saveHiRes() function.

Generally I use this function inside keyPressed() when the ‘s’ key is hit.
Here I save the image at double the original size and exit afterwards.

void keyPressed() {
  if ( key == 's' ) {
    saveHiRes(2);
    exit();
  }
}

The issue that quickly arise it the overwriting of the output image on save.
We could change the filename each time manually but the it’s not really automated. Let’s change that.

Filename checking

I like to save the output images in a folder named ‘img_out’ in the same location of the sketch.

First lets check if the folder exists and create it if it doesn’t.
The sketchPath() function returns the path of the currently executed sketch. It can receive an argument that will be appended to the end of this path.
The File class is a standard Java class that can manipulate files and directory. We use it to check the existence of the folder and create it if needed.

File folder = new File( sketchPath( "img_out" ) );
if ( folder.exists() == false ) {
  folder.mkdir();
}

After this, the output folder exists and we can look for a filename that is not used.
I chose to save my images as ‘output-0.png’, ‘output-1.png’, ‘output-2.png’ etc…

We can check all filename of that form until i find one that is not used.

int i=0;
while( new File( sketchPath("img_out/output-"+i+".png") ).exists() ) {
  i++;
}

When we exit this code block we will have a usable filename.
The last thing to do is to change the save argument in our saveHiRes() function.
The final code now looks like this:

void saveHiRes(int scaleFactor) {
  File folder = new File( sketchPath( "img_out" ) );
  if ( folder.exists() == false ) {
    folder.mkdir();
  }

  int i=0;
  while( new File( sketchPath("img_out/output-"+i+".png") ).exists() ) {
    i++;
  }

  PGraphics hires = createGraphics(width*scaleFactor, height*scaleFactor, JAVA2D);
  beginRecord(hires);
  hires.scale(scaleFactor);
  setup();
  draw();
  endRecord();
  hires.save( sketchPath( "img_out/output-"+i+".png") );
}

With this function, saving hi resolution images becomes really easy and the previously saved images will not be overwritten.