Processing Cover Fill Image

I am working with some of my friends to create a point-and-click choose-your-own-adventure style game using Processing. If you haven't heard of it, Processing, is a great programming environment based on Java that allows you to easily create interactive visual programs and interact with hardware such as the Arduino. One popular use is for data visualization. Anyway, I wanted full page backgrounds in the game. The function background() doesn't work - it requires that the image be the same size as the window, which can sometimes be managed, but what if I want, for example, a scrolling background? I can simply insert an image using image(img_object, xpos, ypos) , but then if the window is larger than my image, there will be blank space. image(img_object, 0, 0, width, height) is no use, it will just stretch the image. If only Processing had the equivalent of background-size: cover! So what could I do? I had to write my own:

PImage img; // initialize it to an image at some point

float factor = 1.0;
if(img.width < width && (width - img.width) > (height - img.height)) {
  factor = (float)width / (float)img.width;
  }
else if(img.height < height && (height - img.height) >= (width - img.width)) {
  factor = (float)height / (float)img.height;
  }
else if(img.width > width && (img.width - width) < (img.height - height)) {
  factor = (float)width / (float)img.width;
  }
else if(img.height > height && (img.height - height) < (img.width - width)) {
  factor = (float)height / (float)img.height;
  }

image(img, 0, 0, img.width * factor, img.height * factor);

Surprisingly simple, right? Basically we find a scale factor and multiply the width and height of the image by that number to retain its proportions. Lets walk through the code

if(img.width < width && (width - img.width) > (height - img.height)) {
  factor = (float)width / (float)img.width;
  }

This checks to see if the width of the image is too small and is farther away from the correct dimensions than the height is. This ensures that we scale up the correct dimension, otherwise we would still have white-space. If these conditions are met then we set the factor to the width of the window divided by the image width. When the image width is multiplied by this factor it will fit the window size exactly. The next else-if is the same, except for height. The last two if statements are for when the image is already too large. In this case we want to scale down the coordinate that is closest to being correct, otherwise we will introduce white-space.

The last line of code simply displays the image, with each the coordinates multiplied by the scale factor. Problem solved! Mostly... Now my image will always fit but it is always aligned to the top left corner. This is much easier to fix.

For a top right alignment, we simply call

image(img, width - img.width * factor, 0, img.width * factor, img.height * factor);

This pushes the image over a full screen, then pulls it back the width of the image. The process is similar for bottom right alignment

image(img, width - img.width * factor, height - img.height * factor, img.width * factor, img.height * factor);

and bottom left

image(img, 0, height - img.height * factor, img.width * factor, img.height * factor);

However, what I really needed was to center the image. A few more lines of code, but not difficult.

float xoffset = 0.0;
float yoffset = 0.0;
if(img.width * factor > width) {
  xoffset = (img.width * factor - width) / 2;
  }
if(img.height * factor > height) {
  yoffset = (img.height * factor - height) / 2;
  }
xoffset = -xoffset;
yoffset = -yoffset;

All it does is it finds the extra margin between the edge of the image and the window edge and cuts that in half. Those variables can then be used like this:

image(img, xoffset, yoffset, img.width * factor, img.height * factor);

The reason for

xoffset = -xoffset;
yoffset = -yoffset;

is that Processing measures everything from the top left corner, so we need to go the opposite direction, which is negative.

Congratulations! You now have a feat that could only be replicated with

background-size: cover;

I do hope that Processing will hurry up and support a few more features, but I will use it anyways.