Glitching Images in Processing
This summer I'm going to release a new album of solo electronic music that is heavily influenced by EDM and classic rock. For the past few weeks I've been trying to figure out what to do about the art on the new album.
The album is called "FYNIX Fights Back" so I decided to use images of animals fighting, but I didn't just want to pull up generic images. I wanted to add my own special touch to each image. So I pulled out a tool that I haven't used in years: the Processing programming language.
Processing is great for simple algorithmic art. In this case I wanted to glitch some images interactively, but I wasn't exactly sure what I wanted to do.
So I just started experimenting. I played with colors and shapes and randomness. I like to derive randomess based on mouse movement. The more the mouse moves, the more randomness is imparted to whatever the user is doing.
I added image glitching based on mouse speed. The quicker the cursor moves, the more random are the generated shapes, and the more they are offset from their source position in the original image.
Here's the end result.
Here's the source code. Make it even better.
import java.awt.event.KeyEvent;
// relationship between image size and canvas size
float scalingFactor = 2.0;
// image from unsplash.com
String imageFilename = "YOUR_FILE_IN_DATA_FOLDER.jpg";
// image container
PImage img;
PImage scaledImage;
int minimumVelocity = 40;
int velocityFactorDivisor = 5; // the larger this is, the more vertices you will get
int velocityFactor = 1; // this will be overridden below
float minimumImageSize = 10.0f;
boolean firstDraw = true;
int currentImage = 1;
void settings() {
// load the source image
img = loadImage(imageFilename);
// load the pixel colors into the pixels array
img.loadPixels();
// create a canvas that is proportional to the selected image
size((int)(img.width / scalingFactor), (int)(img.height / scalingFactor));
// scale the image for the window size
scaledImage = loadImage(imageFilename);
scaledImage.resize(width, height);
// override velocityFactor
velocityFactor = (int)(width / velocityFactorDivisor);
}
void setup() {
// disable lines
noStroke();
}
void keyPressed() {
if(keyCode == KeyEvent.VK_R) {
firstDraw = true; // if the user presses ENTER, then reset
}
}
void draw() {
if(firstDraw) {
image(scaledImage, 0, 0);
firstDraw = false;
}
// right click to render to image file
if(mousePressed && mouseButton == RIGHT) {
save(imageFilename.replace(".jpg", "") + "_render_" + currentImage + ".tga");
currentImage++;
}
if(mousePressed && mouseButton == LEFT && mouseX >= 0 && mouseX < width && mouseY >= 0 && mouseY < height) {
int velocityX = minimumVelocity + (3 * velocity(mouseX, pmouseX, width));
int velocityY = minimumVelocity + (3 * velocity(mouseY, pmouseY, height));
color c = img.pixels[mousePositionToPixelCoordinate(mouseX, mouseY)];
int vertexCount = ((3 * velocityFactor) + velocityX + velocityY) / velocityFactor;
int minimumX = mouseX - (velocityX / 2);
int maximumX = mouseX + (velocityX / 2);
int minimumY = mouseY - (velocityY / 2);
int maximumY = mouseY + (velocityY / 2);
PGraphics pg = createGraphics(maximumX - minimumX, maximumY - minimumY);
pg.beginDraw();
pg.noStroke();
pg.fill(c);
// first draw a shape into the buffer
pg.beginShape();
for(int i = 0; i < vertexCount; i++) {
pg.vertex(random(0, pg.width), random(0, pg.height));
}
pg.endShape();
pg.endDraw();
pg.loadPixels();
// then copy image pixels into the shape
// get the upper left coordinate in the source image
int startingCoordinateInSourceImage = mousePositionToPixelCoordinate(minimumX, minimumY);
// get the width of the source image
int sourceImageWidth = (int)(img.width);
// set the offset from the source image
int offsetX = velocity(mouseX, pmouseX, width);
int offsetY = velocity(mouseY, pmouseY, height);
// ensure that the offset doesn't go off the canvas
if(mouseX > width / 2) offsetX *= -1;
if(mouseY > height / 2) offsetY *= -1;
for(int y = 0; y < pg.height; y++) {
for(int x = 0; x < pg.width; x++) {
// calculate the coordinate in the destination image
int newImageY = y * pg.width;
int newImageX = x;
int newImageCoordinate = newImageX + newImageY;
// calculate the location in the source image
//int sourceImageCoordinate = (int)(startingCoordinateInSourceImage + (x * scalingFactor) + ((y * scalingFactor) * sourceImageWidth));
//int sourceImageCoordinate = (int)(startingCoordinateInSourceImage + ((x + offsetX) * scalingFactor) + (((y + offsetY) * scalingFactor) * sourceImageWidth));
int sourceImageX = (int)(((x + offsetX) * scalingFactor));
int sourceImageY = (int)((((y + offsetY) * scalingFactor) * sourceImageWidth));
int sourceImageCoordinate = (int)(startingCoordinateInSourceImage + sourceImageX + sourceImageY);
// ensure the calculated coordinates are within bounds
if(newImageCoordinate > 0 && newImageCoordinate < pg.pixels.length
&& sourceImageCoordinate > 0 && sourceImageCoordinate < img.pixels.length
&& pg.pixels[newImageCoordinate] == c) {
pg.pixels[newImageCoordinate] = img.pixels[sourceImageCoordinate];
}
}
}
image(pg, minimumX, minimumY);
}
}
// convert the mouse position to a coordinate within the source image
int mousePositionToPixelCoordinate(int mouseX, int mouseY) {
return (int)(mouseX * scalingFactor) + (int)((mouseY * scalingFactor) * img.width);
}
// This sort of calculates mouse velocity as relative to canvas size
int velocity(float x1, float x2, float size) {
int val = (int)(Math.abs((x1 - x2) / size) * size);
return val;
}