Lowtech Painting Machine • tim rodenbröker creative coding

Lowtech Painting Machine

Published by Tim on Friday April 7, 2023

Last modified on December 3rd, 2024 at 17:51

A few months ago I made a small app that allows you to create image collages with some kind of paintbrush tool. Basically, the tool works like an ordinary retouching brush: On the left half you see the source, on the right the painted image. The green square marks the image section that can be selected with the mouse.

paintingmachine.pde

PImage[] images;
PShader shade;
PGraphics source;
PGraphics target;

PGraphics comp;

PGraphics artboard;

PImage buffer;
PImage currImage;

int POSTER_W = 580;
int POSTER_H = 810;

float TILES_X = POSTER_W / 5;
float TILES_Y = POSTER_H / 5;

int sx, sy, sw, sh, dx, dy, dw, dh;

float scalar = 1;

float offsetX = 0;
float offsetY = 0;

float coutoutW = 30;

float threshold = 150;

void setup() {
  size(1160, 810, P2D);

  shade = loadShader("halftone.glsl");
  shade.set("pixelsPerRow", 2000);

  source = createGraphics(POSTER_W, POSTER_H);
  target = createGraphics(POSTER_W, POSTER_H, P2D);
  artboard = createGraphics(900, 900);

  comp = createGraphics(POSTER_W, POSTER_H);

  images = new PImage[9];

  images[0] = loadImage("set3/1.jpg");
  images[1] = loadImage("set3/2.jpg");
  images[2] = loadImage("set3/3.jpg");
  images[3] = loadImage("set3/4.jpg");
  images[4] = loadImage("set3/5.jpg");
  images[5] = loadImage("set3/6.jpg");
  images[6] = loadImage("set3/7.jpg");
  images[7] = loadImage("set3/8.jpg");
  images[8] = loadImage("set3/9.jpg");

  currImage = images[int(random(images.length))];
}

void draw() {
  background(#FFFFFF);
  drawSource();
  drawTarget();

  image(source, 0, 0);

  noStroke();


  image(target, POSTER_W, 0);

  noFill();
  stroke(#00ff00);
  strokeWeight(3);
  drawArtboard();

  rect(mouseX, mouseY, sw, sh);
  rect(mouseX + POSTER_W, mouseY, sw, sh);
}

void drawArtboard() {
  artboard.beginDraw();
  artboard.background(0);
  artboard.imageMode(CENTER);
  PImage buffer = target.get();
  artboard.image(buffer,artboard.width/2,artboard.height/2);
  artboard.endDraw();
}

void randomComposition() {
  
  int padding = 50;
  
  comp.beginDraw();
  comp.background(255);

  float diameter = random(300);

  comp.fill(0);
  comp.noStroke();
  comp.ellipse(random(padding,POSTER_W-padding), random(padding,POSTER_H-padding), diameter, diameter);
  comp.stroke(0);
  comp.strokeWeight(8);
  comp.strokeCap(RECT);
  comp.line(random(padding,POSTER_W-padding), random(padding,POSTER_H-padding), random(padding,POSTER_W-padding), random(padding,POSTER_H-padding));
  comp.endDraw();
}

void render() {
  if (frameCount % 10 == 0) {
    artboard.save("out/" + nf(frameCount, 6) + ".png");
  }
}

void drawSource(){
  source.beginDraw();
  source.background(0);
  source.imageMode(CENTER);
  source.push();
  source.translate(source.width/2 + offsetX, source.height/2 + offsetY);
  source.scale(scalar);
  source.image(currImage,0,0);
  source.pop();
  source.endDraw();
}

void drawTarget() {
  target.beginDraw();
  // target.filter(shade);

  buffer = source.get();

  if (frameCount == 1) {
    target.background(#ffffff);
  }

  sx = mouseX;
  sy = mouseY;
  sw = int(coutoutW);
  sh = int(coutoutW);

  dx = mouseX;
  dy = mouseY;
  dw = int(coutoutW);
  dh = int(coutoutW);

  if (mousePressed) {
    target.copy(buffer, sx, sy, sw, sh, dx, dy, dw, dh);
  }
  //target.filter(shade);
  target.endDraw();
}

void keyReleased() {
  if (key == '1') {
    currImage=images[0];
  }
  if (key == '2') {
    currImage=images[1];
  }
  if (key == '3') {
    currImage=images[2];
  }
  if (key == '4') {
    currImage=images[3];
  }
  if (key == '5') {
    currImage=images[4];
  }
  if (key == '6') {
    currImage=images[5];
  }
  if (key == '7') {
    currImage=images[6];
  }
  if (key == '8') {
    currImage=images[7];
  }
  if (key == '9') {
    currImage=images[8];
  }

  if (key == 'r') {
    scalar = random(0.1, 4);
    offsetX = random(-1200, 1200);
    offsetY = random(-1200, 1200);
  }

  if (key == 't') {
    randomComposition();
    currImage = comp.get();
  }
  if (key == 'e') {
    scalar = 1;
    offsetX = 0;
    offsetY = 0;
  }

  if (keyCode == 37) {
    offsetX-=10;
  }

  if (keyCode == 39) {
    offsetX+=10;
  }
  if (keyCode == 40) {
    offsetY+=10;
  }

  if (keyCode == 38) {
    offsetY-=10;
  }

  if (key == '+') {
    scalar+=0.1;
  }

  if (key == '-') {
    scalar-=0.1;
  }
}

void mouseWheel(MouseEvent event) {
  float e = event.getCount();
  
}

Related

Bi-Weekly Update

Active patrons in a paid tier get regular updates on new content, lessons and courses. For learners. On Fridays, 4pm CET.

Monthly Newsletter

Personal Reflections on Creative Coding, Design and life with Technology, every first Friday of the month, directly to your inbox.

Related

The Story of 128KB

One day in January 2024, I was lethargically scrolling through my Instagram feed on my laptop. And, as so often […]

p5studio

A prototype for a browser-based design-application, built with p5.js and vue.js.

Building Tools with p5.js (Playlist)

Click here to login or connect!To view this content, you must be a member of Tim's Patreon at €7 or […]

DEMO 2025 – My Submissions

Limitations have always been playing a major role in my creative work; I was only able to develop my best […]

The 128kb Framework and its Aesthetic Characteristics

One day in early 2024 I started to experiment with a new idea. I wrote down a set of rules […]

My new writing project “downgrade” is live

Hey folks, I hope you are doing great! You may have already read one or two of my essays that […]

Join the 128kb challenge!

Instagram, Twitter, TikTok… All the main platforms that technically have the required features to connect emerging communies for Creative Coding […]

A custom Mockup Tool, built with Processing (updated)

For my students at Elisava, I have created a new version of my mockup-tool. You need two different files for […]