Draw to off-screen buffer

From Processing

Jump to: navigation, search
Versions: 1.0+
Contributors: matt.patey, REAS
Started: 2008-04-30


Processing offers the use of an off-screen buffer, which can be very useful for creating masking effects. In this hack I outline the basics of writing and reading pixels from an off-screen buffer and suggest some practical ways of applying this technique.

Processing offers the createGraphics method to create an off-screen graphics buffer. This buffer is in most ways the same as the buffer used to draw on-screen images, but it is different in that when you draw to it you don't actually see anything on the screen. This can be very handy for many reasons. For example, drawing to an off-screen buffer can save CPU cycles by letting you draw pixels using Processing drawing routines then save those pixels to a PImage, and draw the PImage to the screen instead of re-drawing the image each frame. Another use of off-screen buffers is the ability to mask areas of an image drawn in Processing to give the effect of a 'viewport' or scrollable area.

First you need to create a new buffer. We do this by creating a PGraphics instance via the createGraphics method:

PGraphics buf = createGraphics(500, 500, P3D);

This creates an off-screen buffer (buf) that is 500 pixels wide by 500 pixels high. Drawing to the buffer is no different than drawing to the regular Processing graphics buffer with the exception that drawing methods must be wrapped between the beginDraw and endDraw methods. For example, if we wanted to draw a rectangle within the off-screen buffer we'd write something like this:

buf.beginDraw();
buf.rectMode(CORNER);
buf.noStroke();
buf.fill(255);
buf.rect(0, 0, 100, 100);
buf.endDraw();

Source Code

/**
 * offscreenComplex.pde
 *
 * Demonstrates how off-screen buffers can be used to maintain a
 * high frame-rate even while drawing complex imagery that normally
 * causes the frame rate to plummet significantly.
 *
 * @author Matt Patey
 */
PGraphics buffer;
PImage img;
PFont font;
boolean mode;
int targetFrameRate;
 
void setup() {
  size(500, 500);
  targetFrameRate = 30;
  frameRate(targetFrameRate);
 
  // Create an off-screen buffer.
  buffer = createGraphics(500, 500, JAVA2D);
 
  // Draw something complex in the off-screen buffer.
  renderComplexImage(buffer);
 
  font = loadFont("CourierNew36.vlw");
 
  // Copy some pixels from the off-screen buffer to display
  // them on the screen.
  updateBuffer();
}
 
/**
 * Populates the off-screen buffer with a complex image then copies
 * the buffer contents to an image that we will display on screen.
 */
void updateBuffer() {
  renderComplexImage(buffer);
  img = buffer.get(0, 0, buffer.width, buffer.height);
}
 
void keyPressed() {
  mode = !mode;
}
 
void mousePressed() {
  updateBuffer();
}
 
void draw() {
  background(255);
 
  if(!mode) {
    image(img, 0, 0);
  } 
  else {
    renderComplexImage(buffer);
    image(img, 0, 0);
  }
 
  // We can still animate things on the main canvas.
  noStroke();
  fill(255, 0, 0, 128);
  ellipse(random(width), random(height), 20, 20);
 
  drawInfo();
}
 
/**
 * Draws a complex drawing into an off-screen buffer.
 */
void renderComplexImage(PGraphics buffer) {
  buffer.beginDraw();
  buffer.background(255);
  buffer.smooth();
  buffer.noFill();
 
  for(int i = 0; i < buffer.width; i++) {
    for(int j = 0; j < buffer.height; j++) {
      if(i % 8 == 0 && j % 8 == 0) {
        buffer.stroke(random(30) + 80, random(30) + 40, 0, random(60) + 24);
        float x = width / 2 + (random(60) - 30);
        float y = 0;
        float x2 = x + (random(160) - 80);
        float y2 = random(height / 2) + height / 2;
 
        buffer.bezier(x, y, 
                      x + (random(x/2) - x/2), y + (random(y/2) - y/2),
                      x2 + (random(x2/2) - x2/2), y2 + (random(y2/2) - y2/2), 
                      x2, y2);
      }
    }
  }
 
  buffer.endDraw();
}
 
/**
 * Draws information about the frame rate and mode.
 */
void drawInfo() {
  fill(0);
  textFont(font);
  textSize(18);
  text("are we re-drawing every frame: " + mode, 5, height - 40);
  text("target frame rate: " + targetFrameRate, 5, height - 25);
  text("current frame rate: " + round(frameRate), 5, height - 10);
}

Downloads

Related Links

Personal tools